mirror of
https://github.com/junegunn/fzf
synced 2026-08-02 18:41:41 +00:00
Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f97d275413 | |||
| 3acb4ca90e | |||
| e86b81bbf5 | |||
| a5447b8b75 | |||
| 7ce6452d83 | |||
| 5643a306bd | |||
| 3c877c504b | |||
| 892d1acccb | |||
| 1a9c282f76 | |||
| fd1ba46f77 | |||
| a4745626dd | |||
| 17bb7ad278 | |||
| 152988c17b | |||
| 4cd37fc02b | |||
| 69b9d674a3 | |||
| bad8061547 | |||
| 62963dcefd | |||
| 68a35e4735 | |||
| 9b9ad77e1c | |||
| 118b4d4a01 | |||
| da14ab6f16 | |||
| 09a4ca6ab5 | |||
| 8a2df79711 | |||
| c30e486b64 | |||
| a575c0c54b | |||
| 77fe96ac0d | |||
| 5234c3759a | |||
| 41b3511ad9 | |||
| 128e4a2e8d | |||
| 07ac90d798 | |||
| 7de87a9b2c | |||
| dff865239a | |||
| 07f8f70c5b | |||
| f625c5aabe | |||
| 8a74976c1f | |||
| b6bfd4a5cb | |||
| 008fb9d258 | |||
| db6db49ed6 | |||
| 05453881c3 | |||
| 5e47ab9431 | |||
| ec70acd0b9 | |||
| 25e61056b6 | |||
| d579e335b5 | |||
| 7e344ceb85 | |||
| 0145b82ea0 | |||
| b4efe7aab7 | |||
| 9ffe951f6d | |||
| a5ea4f57bd | |||
| 88f4c16755 | |||
| c7ee071efa | |||
| 0740ef7ceb | |||
| b29bd809ac | |||
| 8977c9257a |
@@ -1,22 +0,0 @@
|
|||||||
<!-- ISSUES NOT FOLLOWING THIS TEMPLATE WILL BE CLOSED AND DELETED -->
|
|
||||||
|
|
||||||
<!-- Check all that apply [x] -->
|
|
||||||
|
|
||||||
- [ ] I have read through the manual page (`man fzf`)
|
|
||||||
- [ ] I have the latest version of fzf
|
|
||||||
- [ ] I have searched through the existing issues
|
|
||||||
|
|
||||||
## Info
|
|
||||||
|
|
||||||
- OS
|
|
||||||
- [ ] Linux
|
|
||||||
- [ ] Mac OS X
|
|
||||||
- [ ] Windows
|
|
||||||
- [ ] Etc.
|
|
||||||
- Shell
|
|
||||||
- [ ] bash
|
|
||||||
- [ ] zsh
|
|
||||||
- [ ] fish
|
|
||||||
|
|
||||||
## Problem / Steps to reproduce
|
|
||||||
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
name: Issue Template
|
||||||
|
description: Report a problem or bug related to fzf to help us improve
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: ISSUES NOT FOLLOWING THIS TEMPLATE WILL BE CLOSED AND DELETED
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
options:
|
||||||
|
- label: I have read through the manual page (`man fzf`)
|
||||||
|
required: true
|
||||||
|
- label: I have searched through the existing issues
|
||||||
|
required: true
|
||||||
|
- label: For bug reports, I have checked if the bug is reproducible in the latest version of fzf
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Output of `fzf --version`
|
||||||
|
placeholder: e.g. 0.48.1 (d579e33)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: OS
|
||||||
|
options:
|
||||||
|
- label: Linux
|
||||||
|
- label: macOS
|
||||||
|
- label: Windows
|
||||||
|
- label: Etc.
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Shell
|
||||||
|
options:
|
||||||
|
- label: bash
|
||||||
|
- label: zsh
|
||||||
|
- label: fish
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Problem / Steps to reproduce
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: "1.20"
|
||||||
|
|
||||||
- name: Setup Ruby
|
- name: Setup Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.18
|
go-version: "1.20"
|
||||||
|
|
||||||
- name: Setup Ruby
|
- name: Setup Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ builds:
|
|||||||
cat > /tmp/fzf-gon-amd64.hcl << EOF
|
cat > /tmp/fzf-gon-amd64.hcl << EOF
|
||||||
source = ["./dist/fzf-macos_darwin_amd64_v1/fzf"]
|
source = ["./dist/fzf-macos_darwin_amd64_v1/fzf"]
|
||||||
bundle_id = "kr.junegunn.fzf"
|
bundle_id = "kr.junegunn.fzf"
|
||||||
apple_id {
|
|
||||||
username = "junegunn.c@gmail.com"
|
|
||||||
password = "@env:AC_PASSWORD"
|
|
||||||
}
|
|
||||||
sign {
|
sign {
|
||||||
application_identity = "Developer ID Application: Junegunn Choi (Y254DRW44Z)"
|
application_identity = "Developer ID Application: Junegunn Choi (Y254DRW44Z)"
|
||||||
}
|
}
|
||||||
@@ -48,10 +44,6 @@ builds:
|
|||||||
cat > /tmp/fzf-gon-arm64.hcl << EOF
|
cat > /tmp/fzf-gon-arm64.hcl << EOF
|
||||||
source = ["./dist/fzf-macos-arm_darwin_arm64/fzf"]
|
source = ["./dist/fzf-macos-arm_darwin_arm64/fzf"]
|
||||||
bundle_id = "kr.junegunn.fzf"
|
bundle_id = "kr.junegunn.fzf"
|
||||||
apple_id {
|
|
||||||
username = "junegunn.c@gmail.com"
|
|
||||||
password = "@env:AC_PASSWORD"
|
|
||||||
}
|
|
||||||
sign {
|
sign {
|
||||||
application_identity = "Developer ID Application: Junegunn Choi (Y254DRW44Z)"
|
application_identity = "Developer ID Application: Junegunn Choi (Y254DRW44Z)"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Build instructions
|
|||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- Go 1.18 or above
|
- Go 1.20 or above
|
||||||
|
|
||||||
### Using Makefile
|
### Using Makefile
|
||||||
|
|
||||||
@@ -24,13 +24,23 @@ make build
|
|||||||
make release
|
make release
|
||||||
```
|
```
|
||||||
|
|
||||||
> :warning: Makefile uses git commands to determine the version and the
|
> [!WARNING]
|
||||||
> revision information for `fzf --version`. So if you're building fzf from an
|
> Makefile uses git commands to determine the version and the revision
|
||||||
|
> information for `fzf --version`. So if you're building fzf from an
|
||||||
> environment where its git information is not available, you have to manually
|
> environment where its git information is not available, you have to manually
|
||||||
> set `$FZF_VERSION` and `$FZF_REVISION`.
|
> set `$FZF_VERSION` and `$FZF_REVISION`.
|
||||||
>
|
>
|
||||||
> e.g. `FZF_VERSION=0.24.0 FZF_REVISION=tarball make`
|
> e.g. `FZF_VERSION=0.24.0 FZF_REVISION=tarball make`
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> To build fzf with profiling options enabled, set `TAGS=pprof`
|
||||||
|
>
|
||||||
|
> ```sh
|
||||||
|
> TAGS=pprof make clean install
|
||||||
|
> fzf --profile-cpu /tmp/cpu.pprof --profile-mem /tmp/mem.pprof \
|
||||||
|
> --profile-block /tmp/block.pprof --profile-mutex /tmp/mutex.pprof
|
||||||
|
> ```
|
||||||
|
|
||||||
Third-party libraries used
|
Third-party libraries used
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,88 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
0.50.0
|
||||||
|
------
|
||||||
|
- Search performance optimization. You can observe 50%+ improvement in some scenarios.
|
||||||
|
```
|
||||||
|
$ rg --line-number --no-heading --smart-case . > $DATA
|
||||||
|
|
||||||
|
$ wc < $DATA
|
||||||
|
5520118 26862362 897487793
|
||||||
|
|
||||||
|
$ hyperfine -w 1 -L bin fzf-0.49.0,fzf-7ce6452,fzf-a5447b8,fzf '{bin} --filter "///" < $DATA | head -30'
|
||||||
|
Summary
|
||||||
|
fzf --filter "///" < $DATA | head -30 ran
|
||||||
|
1.16 ± 0.03 times faster than fzf-a5447b8 --filter "///" < $DATA | head -30
|
||||||
|
1.23 ± 0.03 times faster than fzf-7ce6452 --filter "///" < $DATA | head -30
|
||||||
|
1.52 ± 0.03 times faster than fzf-0.49.0 --filter "///" < $DATA | head -30
|
||||||
|
```
|
||||||
|
- Added `jump` and `jump-cancel` events that are triggered when leaving `jump` mode
|
||||||
|
```sh
|
||||||
|
# Default behavior
|
||||||
|
fzf --bind space:jump
|
||||||
|
|
||||||
|
# Same as jump-accept action
|
||||||
|
fzf --bind space:jump,jump:accept
|
||||||
|
|
||||||
|
# Accept on jump, abort on cancel
|
||||||
|
fzf --bind space:jump,jump:accept,jump-cancel:abort
|
||||||
|
|
||||||
|
# Change header on jump-cancel
|
||||||
|
fzf --bind 'space:change-header(Type jump label)+jump,jump-cancel:change-header:Jump cancelled'
|
||||||
|
```
|
||||||
|
- Added a new environment variable `$FZF_KEY` exported to the child processes. It's the name of the last key pressed.
|
||||||
|
```sh
|
||||||
|
fzf --bind 'space:jump,jump:accept,jump-cancel:transform:[[ $FZF_KEY =~ ctrl-c ]] && echo abort'
|
||||||
|
```
|
||||||
|
- fzf can be built with profiling options. See [BUILD.md](BUILD.md) for more information.
|
||||||
|
- Bug fixes
|
||||||
|
|
||||||
|
0.49.0
|
||||||
|
------
|
||||||
|
- Ingestion performance improved by around 40% (more or less depending on options)
|
||||||
|
- `--info=hidden` and `--info=inline-right` will no longer hide the horizontal separator by default. This gives you more flexibility in customizing the layout.
|
||||||
|
```sh
|
||||||
|
fzf --border --info=inline-right
|
||||||
|
fzf --border --info=inline-right --separator ═
|
||||||
|
fzf --border --info=inline-right --no-separator
|
||||||
|
fzf --border --info=hidden
|
||||||
|
fzf --border --info=hidden --separator ━
|
||||||
|
fzf --border --info=hidden --no-separator
|
||||||
|
```
|
||||||
|
- Added two environment variables exported to the child processes
|
||||||
|
- `FZF_PREVIEW_LABEL`
|
||||||
|
- `FZF_BORDER_LABEL`
|
||||||
|
```sh
|
||||||
|
# Use the current value of $FZF_PREVIEW_LABEL to determine which actions to perform
|
||||||
|
git ls-files |
|
||||||
|
fzf --header 'Press CTRL-P to change preview mode' \
|
||||||
|
--bind='ctrl-p:transform:[[ $FZF_PREVIEW_LABEL =~ cat ]] \
|
||||||
|
&& echo "change-preview(git log --color=always \{})+change-preview-label([[ log ]])" \
|
||||||
|
|| echo "change-preview(bat --color=always \{})+change-preview-label([[ cat ]])"'
|
||||||
|
```
|
||||||
|
- Renamed `track` action to `track-current` to highlight the difference between the global tracking state set by `--track` and a one-off tracking action
|
||||||
|
- `track` is still available as an alias
|
||||||
|
- Added `untrack-current` and `toggle-track-current` actions
|
||||||
|
- `*-current` actions are no-op when the global tracking state is set
|
||||||
|
- Bug fixes and minor improvements
|
||||||
|
|
||||||
|
0.48.1
|
||||||
|
------
|
||||||
|
- CTRL-T and ALT-C bindings can be disabled by setting `FZF_CTRL_T_COMMAND` and `FZF_ALT_C_COMMAND` to empty strings respectively when sourcing the script
|
||||||
|
```sh
|
||||||
|
# bash
|
||||||
|
FZF_CTRL_T_COMMAND= FZF_ALT_C_COMMAND= eval "$(fzf --bash)"
|
||||||
|
|
||||||
|
# zsh
|
||||||
|
FZF_CTRL_T_COMMAND= FZF_ALT_C_COMMAND= eval "$(fzf --zsh)"
|
||||||
|
|
||||||
|
# fish
|
||||||
|
fzf --fish | FZF_CTRL_T_COMMAND= FZF_ALT_C_COMMAND= source
|
||||||
|
```
|
||||||
|
- Setting the variables after sourcing the script will have no effect
|
||||||
|
- Bug fixes
|
||||||
|
|
||||||
0.48.0
|
0.48.0
|
||||||
------
|
------
|
||||||
- Shell integration scripts are now embedded in the fzf binary. This simplifies the distribution, and the users are less likely to have problems caused by using incompatible scripts and binaries.
|
- Shell integration scripts are now embedded in the fzf binary. This simplifies the distribution, and the users are less likely to have problems caused by using incompatible scripts and binaries.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ GOOS ?= $(word 1, $(subst /, " ", $(word 4, $(shell go version))))
|
|||||||
|
|
||||||
MAKEFILE := $(realpath $(lastword $(MAKEFILE_LIST)))
|
MAKEFILE := $(realpath $(lastword $(MAKEFILE_LIST)))
|
||||||
ROOT_DIR := $(shell dirname $(MAKEFILE))
|
ROOT_DIR := $(shell dirname $(MAKEFILE))
|
||||||
SOURCES := $(wildcard *.go src/*.go src/*/*.go) $(MAKEFILE)
|
SOURCES := $(wildcard *.go src/*.go src/*/*.go shell/*sh) $(MAKEFILE)
|
||||||
|
|
||||||
ifdef FZF_VERSION
|
ifdef FZF_VERSION
|
||||||
VERSION := $(FZF_VERSION)
|
VERSION := $(FZF_VERSION)
|
||||||
@@ -79,6 +79,7 @@ all: target/$(BINARY)
|
|||||||
test: $(SOURCES)
|
test: $(SOURCES)
|
||||||
[ -z "$$(gofmt -s -d src)" ] || (gofmt -s -d src; exit 1)
|
[ -z "$$(gofmt -s -d src)" ] || (gofmt -s -d src; exit 1)
|
||||||
SHELL=/bin/sh GOOS= $(GO) test -v -tags "$(TAGS)" \
|
SHELL=/bin/sh GOOS= $(GO) test -v -tags "$(TAGS)" \
|
||||||
|
github.com/junegunn/fzf \
|
||||||
github.com/junegunn/fzf/src \
|
github.com/junegunn/fzf/src \
|
||||||
github.com/junegunn/fzf/src/algo \
|
github.com/junegunn/fzf/src/algo \
|
||||||
github.com/junegunn/fzf/src/tui \
|
github.com/junegunn/fzf/src/tui \
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
module github.com/junegunn/fzf
|
module github.com/junegunn/fzf
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/charlievieth/fastwalk v1.0.2
|
github.com/charlievieth/fastwalk v1.0.3
|
||||||
github.com/gdamore/tcell/v2 v2.7.4
|
github.com/gdamore/tcell/v2 v2.7.4
|
||||||
github.com/mattn/go-isatty v0.0.20
|
github.com/mattn/go-isatty v0.0.20
|
||||||
github.com/mattn/go-shellwords v1.0.12
|
github.com/mattn/go-shellwords v1.0.12
|
||||||
github.com/rivo/uniseg v0.4.7
|
github.com/rivo/uniseg v0.4.7
|
||||||
golang.org/x/sys v0.18.0
|
golang.org/x/sys v0.19.0
|
||||||
golang.org/x/term v0.18.0
|
golang.org/x/term v0.19.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -17,4 +17,4 @@ require (
|
|||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.17
|
go 1.20
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
github.com/charlievieth/fastwalk v1.0.2 h1:KYWo7xszmoldOGrwdNIeznSzhj9mhgk+6DwHunG99bc=
|
github.com/charlievieth/fastwalk v1.0.3 h1:eNWFaNPe5srPqQ5yyDbhAf11paeZaHWcihRhpuYFfSg=
|
||||||
github.com/charlievieth/fastwalk v1.0.2/go.mod h1:JSfglY/gmL/rqsUS1NCsJTocB5n6sSl9ApAqif4CUbs=
|
github.com/charlievieth/fastwalk v1.0.3/go.mod h1:JSfglY/gmL/rqsUS1NCsJTocB5n6sSl9ApAqif4CUbs=
|
||||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
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/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||||
github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=
|
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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.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.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.19.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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
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.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||||
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=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
version=0.48.0
|
version=0.50.0
|
||||||
auto_completion=
|
auto_completion=
|
||||||
key_bindings=
|
key_bindings=
|
||||||
update_config=2
|
update_config=2
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
$version="0.48.0"
|
$version="0.50.0"
|
||||||
|
|
||||||
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/junegunn/fzf/src/protector"
|
"github.com/junegunn/fzf/src/protector"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "0.48"
|
var version string = "0.50"
|
||||||
var revision string = "devel"
|
var revision string = "devel"
|
||||||
|
|
||||||
//go:embed shell/key-bindings.bash
|
//go:embed shell/key-bindings.bash
|
||||||
|
|||||||
+158
@@ -0,0 +1,158 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/importer"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadPackages(t *testing.T) []*build.Package {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pkgs []*build.Package
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
err = filepath.WalkDir(wd, func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name := d.Name()
|
||||||
|
if d.IsDir() {
|
||||||
|
if name == "" || name[0] == '.' || name[0] == '_' || name == "vendor" || name == "tmp" {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if d.Type().IsRegular() && filepath.Ext(name) == ".go" && !strings.HasSuffix(name, "_test.go") {
|
||||||
|
dir := filepath.Dir(path)
|
||||||
|
if !seen[dir] {
|
||||||
|
pkg, err := build.ImportDir(dir, build.ImportComment)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %s", dir, err)
|
||||||
|
}
|
||||||
|
if pkg.ImportPath == "" || pkg.ImportPath == "." {
|
||||||
|
importPath, err := filepath.Rel(wd, dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pkg.ImportPath = filepath.ToSlash(filepath.Join("github.com/junegunn/fzf", importPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgs = append(pkgs, pkg)
|
||||||
|
seen[dir] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(pkgs, func(i, j int) bool {
|
||||||
|
return pkgs[i].ImportPath < pkgs[j].ImportPath
|
||||||
|
})
|
||||||
|
return pkgs
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceImporter = importer.ForCompiler(token.NewFileSet(), "source", nil)
|
||||||
|
|
||||||
|
func checkPackageForOsExit(t *testing.T, bpkg *build.Package, allowed map[string]int) (errOsExit bool) {
|
||||||
|
var files []*ast.File
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
for _, name := range bpkg.GoFiles {
|
||||||
|
filename := filepath.Join(bpkg.Dir, name)
|
||||||
|
af, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
files = append(files, af)
|
||||||
|
}
|
||||||
|
|
||||||
|
info := types.Info{
|
||||||
|
Uses: make(map[*ast.Ident]types.Object),
|
||||||
|
}
|
||||||
|
conf := types.Config{
|
||||||
|
Importer: sourceImporter,
|
||||||
|
}
|
||||||
|
_, err := conf.Check(bpkg.Name, fset, files, &info)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for id, obj := range info.Uses {
|
||||||
|
if obj.Pkg() != nil && obj.Pkg().Name() == "os" && obj.Name() == "Exit" {
|
||||||
|
pos := fset.Position(id.Pos())
|
||||||
|
|
||||||
|
name, err := filepath.Rel(wd, pos.Filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
name = pos.Filename
|
||||||
|
}
|
||||||
|
name = filepath.ToSlash(name)
|
||||||
|
|
||||||
|
// Check if the usage is allowed
|
||||||
|
if allowed[name] > 0 {
|
||||||
|
allowed[name]--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Errorf("os.Exit referenced at: %s:%d:%d", name, pos.Line, pos.Column)
|
||||||
|
errOsExit = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errOsExit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce that src/util.Exit() is used instead of os.Exit by prohibiting
|
||||||
|
// references to it anywhere else in the fzf code base.
|
||||||
|
func TestOSExitNotAllowed(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping: short test")
|
||||||
|
}
|
||||||
|
allowed := map[string]int{
|
||||||
|
"src/util/atexit.go": 1, // os.Exit allowed 1 time in "atexit.go"
|
||||||
|
}
|
||||||
|
var errOsExit bool
|
||||||
|
for _, pkg := range loadPackages(t) {
|
||||||
|
t.Run(pkg.ImportPath, func(t *testing.T) {
|
||||||
|
if checkPackageForOsExit(t, pkg, allowed) {
|
||||||
|
errOsExit = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if t.Failed() && errOsExit {
|
||||||
|
var names []string
|
||||||
|
for name := range allowed {
|
||||||
|
names = append(names, fmt.Sprintf("%q", name))
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
|
||||||
|
const errMsg = `
|
||||||
|
Test failed because os.Exit was referenced outside of the following files:
|
||||||
|
|
||||||
|
%s
|
||||||
|
|
||||||
|
Use github.com/junegunn/fzf/src/util.Exit() instead to exit the program.
|
||||||
|
This is enforced because calling os.Exit() prevents the functions
|
||||||
|
registered with util.AtExit() from running.`
|
||||||
|
|
||||||
|
t.Errorf(errMsg, strings.Join(names, "\n "))
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -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 "Mar 2024" "fzf 0.48.0" "fzf-tmux - open fzf in tmux split pane"
|
.TH fzf-tmux 1 "Apr 2024" "fzf 0.50.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
|
||||||
|
|||||||
+51
-22
@@ -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 "Mar 2024" "fzf 0.48.0" "fzf - a command-line fuzzy finder"
|
.TH fzf 1 "Apr 2024" "fzf 0.50.0" "fzf - a command-line fuzzy finder"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
fzf - a command-line fuzzy finder
|
fzf - a command-line fuzzy finder
|
||||||
@@ -191,7 +191,7 @@ actions are affected:
|
|||||||
\fBkill-word\fR
|
\fBkill-word\fR
|
||||||
.TP
|
.TP
|
||||||
.BI "--jump-labels=" "CHARS"
|
.BI "--jump-labels=" "CHARS"
|
||||||
Label characters for \fBjump\fR and \fBjump-accept\fR
|
Label characters for \fBjump\fR mode.
|
||||||
.SS Layout
|
.SS Layout
|
||||||
.TP
|
.TP
|
||||||
.BI "--height=" "[~]HEIGHT[%]"
|
.BI "--height=" "[~]HEIGHT[%]"
|
||||||
@@ -372,20 +372,21 @@ e.g.
|
|||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "--info=" "STYLE"
|
.BI "--info=" "STYLE"
|
||||||
Determines the display style of finder info (match counters).
|
Determines the display style of the finder info. (e.g. match counter, loading indicator, etc.)
|
||||||
|
|
||||||
|
.BR default " On the left end of the horizontal separator"
|
||||||
.br
|
.br
|
||||||
.BR default " Display on the next line to the prompt"
|
.BR right " On the right end of the horizontal separator"
|
||||||
.br
|
.br
|
||||||
.BR right " Display on the right end of the next line to the prompt"
|
.BR hidden " Do not display finder info"
|
||||||
.br
|
.br
|
||||||
.BR inline " Display on the same line with the default separator ' < '"
|
.BR inline " After the prompt with the default prefix ' < '"
|
||||||
.br
|
.br
|
||||||
.BR inline:SEPARATOR " Display on the same line with a non-default separator"
|
.BR inline:PREFIX " After the prompt with a non-default prefix"
|
||||||
.br
|
.br
|
||||||
.BR inline-right " Display on the right end of the same line
|
.BR inline-right " On the right end of the prompt line"
|
||||||
.br
|
.br
|
||||||
.BR hidden " Do not display finder info"
|
.BR inline-right:PREFIX " On the right end of the prompt line with a custom prefix"
|
||||||
.br
|
.br
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
@@ -975,8 +976,14 @@ fzf exports the following environment variables to its child processes.
|
|||||||
.br
|
.br
|
||||||
.BR FZF_PROMPT " Prompt string"
|
.BR FZF_PROMPT " Prompt string"
|
||||||
.br
|
.br
|
||||||
|
.BR FZF_PREVIEW_LABEL " Preview label string"
|
||||||
|
.br
|
||||||
|
.BR FZF_BORDER_LABEL " Border label string"
|
||||||
|
.br
|
||||||
.BR FZF_ACTION " The name of the last action performed"
|
.BR FZF_ACTION " The name of the last action performed"
|
||||||
.br
|
.br
|
||||||
|
.BR FZF_KEY " The name of the last key pressed"
|
||||||
|
.br
|
||||||
.BR FZF_PORT " Port number when --listen option is used"
|
.BR FZF_PORT " Port number when --listen option is used"
|
||||||
.br
|
.br
|
||||||
|
|
||||||
@@ -1047,21 +1054,21 @@ e.g.
|
|||||||
.br
|
.br
|
||||||
\fIctrl-]\fR
|
\fIctrl-]\fR
|
||||||
.br
|
.br
|
||||||
\fIctrl-^\fR (\fIctrl-6\fR)
|
\fIctrl-^\fR (\fIctrl-6\fR)
|
||||||
.br
|
.br
|
||||||
\fIctrl-/\fR (\fIctrl-_\fR)
|
\fIctrl-/\fR (\fIctrl-_\fR)
|
||||||
.br
|
.br
|
||||||
\fIctrl-alt-[a-z]\fR
|
\fIctrl-alt-[a-z]\fR
|
||||||
.br
|
.br
|
||||||
\fIalt-[*]\fR (Any case-sensitive single character is allowed)
|
\fIalt-[*]\fR (Any case-sensitive single character is allowed)
|
||||||
.br
|
.br
|
||||||
\fIf[1-12]\fR
|
\fIf[1-12]\fR
|
||||||
.br
|
.br
|
||||||
\fIenter\fR (\fIreturn\fR \fIctrl-m\fR)
|
\fIenter\fR (\fIreturn\fR \fIctrl-m\fR)
|
||||||
.br
|
.br
|
||||||
\fIspace\fR
|
\fIspace\fR
|
||||||
.br
|
.br
|
||||||
\fIbspace\fR (\fIbs\fR)
|
\fIbackspace\fR (\fIbspace\fR \fIbs\fR)
|
||||||
.br
|
.br
|
||||||
\fIalt-up\fR
|
\fIalt-up\fR
|
||||||
.br
|
.br
|
||||||
@@ -1075,15 +1082,15 @@ e.g.
|
|||||||
.br
|
.br
|
||||||
\fIalt-space\fR
|
\fIalt-space\fR
|
||||||
.br
|
.br
|
||||||
\fIalt-bspace\fR (\fIalt-bs\fR)
|
\fIalt-backspace\fR (\fIalt-bspace\fR \fIalt-bs\fR)
|
||||||
.br
|
.br
|
||||||
\fItab\fR
|
\fItab\fR
|
||||||
.br
|
.br
|
||||||
\fIbtab\fR (\fIshift-tab\fR)
|
\fIshift-tab\fR (\fIbtab\fR)
|
||||||
.br
|
.br
|
||||||
\fIesc\fR
|
\fIesc\fR
|
||||||
.br
|
.br
|
||||||
\fIdel\fR
|
\fIdelete\fR (\fIdel\fR)
|
||||||
.br
|
.br
|
||||||
\fIup\fR
|
\fIup\fR
|
||||||
.br
|
.br
|
||||||
@@ -1099,9 +1106,9 @@ e.g.
|
|||||||
.br
|
.br
|
||||||
\fIinsert\fR
|
\fIinsert\fR
|
||||||
.br
|
.br
|
||||||
\fIpgup\fR (\fIpage-up\fR)
|
\fIpage-up\fR (\fIpgup\fR)
|
||||||
.br
|
.br
|
||||||
\fIpgdn\fR (\fIpage-down\fR)
|
\fIpage-down\fR (\fIpgdn\fR)
|
||||||
.br
|
.br
|
||||||
\fIshift-up\fR
|
\fIshift-up\fR
|
||||||
.br
|
.br
|
||||||
@@ -1155,6 +1162,7 @@ e.g.
|
|||||||
\fB# Move cursor to the last item and select all items
|
\fB# Move cursor to the last item and select all items
|
||||||
seq 1000 | fzf --multi --sync --bind start:last+select-all\fR
|
seq 1000 | fzf --multi --sync --bind start:last+select-all\fR
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
\fIload\fR
|
\fIload\fR
|
||||||
.RS
|
.RS
|
||||||
Triggered when the input stream is complete and the initial processing of the
|
Triggered when the input stream is complete and the initial processing of the
|
||||||
@@ -1164,6 +1172,7 @@ 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
|
\fIresize\fR
|
||||||
.RS
|
.RS
|
||||||
Triggered when the terminal size is changed.
|
Triggered when the terminal size is changed.
|
||||||
@@ -1171,6 +1180,7 @@ Triggered when the terminal size is changed.
|
|||||||
e.g.
|
e.g.
|
||||||
\fBfzf --bind 'resize:transform-header:echo Resized: ${FZF_COLUMNS}x${FZF_LINES}'\fR
|
\fBfzf --bind 'resize:transform-header:echo Resized: ${FZF_COLUMNS}x${FZF_LINES}'\fR
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
\fIresult\fR
|
\fIresult\fR
|
||||||
.RS
|
.RS
|
||||||
Triggered when the filtering for the current query is complete and the result list is ready.
|
Triggered when the filtering for the current query is complete and the result list is ready.
|
||||||
@@ -1204,6 +1214,7 @@ e.g.
|
|||||||
# Beware not to introduce an infinite loop
|
# Beware not to introduce an infinite loop
|
||||||
seq 10 | fzf --bind 'focus:up' --cycle\fR
|
seq 10 | fzf --bind 'focus:up' --cycle\fR
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
\fIone\fR
|
\fIone\fR
|
||||||
.RS
|
.RS
|
||||||
Triggered when there's only one match. \fBone:accept\fR binding is comparable
|
Triggered when there's only one match. \fBone:accept\fR binding is comparable
|
||||||
@@ -1215,6 +1226,7 @@ e.g.
|
|||||||
\fB# Automatically select the only match
|
\fB# Automatically select the only match
|
||||||
seq 10 | fzf --bind one:accept\fR
|
seq 10 | fzf --bind one:accept\fR
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
\fIzero\fR
|
\fIzero\fR
|
||||||
.RS
|
.RS
|
||||||
Triggered when there's no match. \fBzero:abort\fR binding is comparable to
|
Triggered when there's no match. \fBzero:abort\fR binding is comparable to
|
||||||
@@ -1236,6 +1248,22 @@ e.g.
|
|||||||
\fBfzf --bind backward-eof:abort\fR
|
\fBfzf --bind backward-eof:abort\fR
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
|
\fIjump\fR
|
||||||
|
.RS
|
||||||
|
Triggered when successfully jumped to the target item in \fBjump\fR mode.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
\fBfzf --bind space:jump,jump:accept\fR
|
||||||
|
.RE
|
||||||
|
|
||||||
|
\fIjump-cancel\fR
|
||||||
|
.RS
|
||||||
|
Triggered when \fBjump\fR mode is cancelled.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
\fBfzf --bind space:jump,jump:accept,jump-cancel:abort\fR
|
||||||
|
.RE
|
||||||
|
|
||||||
.SS AVAILABLE ACTIONS:
|
.SS AVAILABLE ACTIONS:
|
||||||
A key or an event can be bound to one or more of the following actions.
|
A key or an event can be bound to one or more of the following actions.
|
||||||
|
|
||||||
@@ -1278,7 +1306,6 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBforward-word\fR \fIalt-f shift-right\fR
|
\fBforward-word\fR \fIalt-f shift-right\fR
|
||||||
\fBignore\fR
|
\fBignore\fR
|
||||||
\fBjump\fR (EasyMotion-like 2-keystroke movement)
|
\fBjump\fR (EasyMotion-like 2-keystroke movement)
|
||||||
\fBjump-accept\fR (jump and accept)
|
|
||||||
\fBkill-line\fR
|
\fBkill-line\fR
|
||||||
\fBkill-word\fR \fIalt-d\fR
|
\fBkill-word\fR \fIalt-d\fR
|
||||||
\fBlast\fR (move to the last match; same as \fBpos(-1)\fR)
|
\fBlast\fR (move to the last match; same as \fBpos(-1)\fR)
|
||||||
@@ -1326,9 +1353,10 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBtoggle-preview-wrap\fR
|
\fBtoggle-preview-wrap\fR
|
||||||
\fBtoggle-search\fR (toggle search functionality)
|
\fBtoggle-search\fR (toggle search functionality)
|
||||||
\fBtoggle-sort\fR
|
\fBtoggle-sort\fR
|
||||||
\fBtoggle-track\fR
|
\fBtoggle-track\fR (toggle global tracking option (\fB--track\fR))
|
||||||
|
\fBtoggle-track-current\fR (toggle tracking of the current item)
|
||||||
\fBtoggle+up\fR \fIbtab (shift-tab)\fR
|
\fBtoggle+up\fR \fIbtab (shift-tab)\fR
|
||||||
\fBtrack\fR (track the current item; automatically disabled if focus changes)
|
\fBtrack-current\fR (track the current item; automatically disabled if focus changes)
|
||||||
\fBtransform(...)\fR (transform states using the output of an external command)
|
\fBtransform(...)\fR (transform states using the output of an external command)
|
||||||
\fBtransform-border-label(...)\fR (transform border label using an external command)
|
\fBtransform-border-label(...)\fR (transform border label using an external command)
|
||||||
\fBtransform-header(...)\fR (transform header using an external command)
|
\fBtransform-header(...)\fR (transform header using an external command)
|
||||||
@@ -1338,6 +1366,7 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBunbind(...)\fR (unbind bindings)
|
\fBunbind(...)\fR (unbind bindings)
|
||||||
\fBunix-line-discard\fR \fIctrl-u\fR
|
\fBunix-line-discard\fR \fIctrl-u\fR
|
||||||
\fBunix-word-rubout\fR \fIctrl-w\fR
|
\fBunix-word-rubout\fR \fIctrl-w\fR
|
||||||
|
\fBuntrack-current\fR (stop tracking the current item; no-op if global tracking is enabled)
|
||||||
\fBup\fR \fIctrl-k ctrl-p up\fR
|
\fBup\fR \fIctrl-k ctrl-p up\fR
|
||||||
\fByank\fR \fIctrl-y\fR
|
\fByank\fR \fIctrl-y\fR
|
||||||
|
|
||||||
|
|||||||
+16
-5
@@ -9,7 +9,7 @@
|
|||||||
# - $FZF_COMPLETION_TRIGGER (default: '**')
|
# - $FZF_COMPLETION_TRIGGER (default: '**')
|
||||||
# - $FZF_COMPLETION_OPTS (default: empty)
|
# - $FZF_COMPLETION_OPTS (default: empty)
|
||||||
|
|
||||||
[[ $- =~ i ]] || return 0
|
if [[ $- =~ i ]]; then
|
||||||
|
|
||||||
|
|
||||||
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
|
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
|
||||||
@@ -291,7 +291,7 @@ _fzf_handle_dynamic_completion() {
|
|||||||
"$REPLY" "$@"
|
"$REPLY" "$@"
|
||||||
elif [[ -n "${_fzf_completion_loader-}" ]]; then
|
elif [[ -n "${_fzf_completion_loader-}" ]]; then
|
||||||
orig_complete=$(complete -p "$orig_cmd" 2> /dev/null)
|
orig_complete=$(complete -p "$orig_cmd" 2> /dev/null)
|
||||||
_completion_loader "$@"
|
$_fzf_completion_loader "$@"
|
||||||
ret=$?
|
ret=$?
|
||||||
# _completion_loader may not have updated completion for the command
|
# _completion_loader may not have updated completion for the command
|
||||||
if [[ "$(complete -p "$orig_cmd" 2> /dev/null)" != "$orig_complete" ]]; then
|
if [[ "$(complete -p "$orig_cmd" 2> /dev/null)" != "$orig_complete" ]]; then
|
||||||
@@ -308,6 +308,7 @@ _fzf_handle_dynamic_completion() {
|
|||||||
eval "$orig_complete"
|
eval "$orig_complete"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
[[ $ret -eq 0 ]] && return 124
|
||||||
return $ret
|
return $ret
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -516,8 +517,16 @@ a_cmds="
|
|||||||
# Preserve existing completion
|
# Preserve existing completion
|
||||||
__fzf_orig_completion < <(complete -p $d_cmds $a_cmds ssh 2> /dev/null)
|
__fzf_orig_completion < <(complete -p $d_cmds $a_cmds ssh 2> /dev/null)
|
||||||
|
|
||||||
if type _completion_loader > /dev/null 2>&1; then
|
if type _comp_load > /dev/null 2>&1; then
|
||||||
_fzf_completion_loader=1
|
# _comp_load was added in bash-completion 2.12 to replace _completion_loader.
|
||||||
|
# We use it without -D option so that it does not use _comp_complete_minimal as the fallback.
|
||||||
|
_fzf_completion_loader=_comp_load
|
||||||
|
elif type __load_completion > /dev/null 2>&1; then
|
||||||
|
# In bash-completion 2.11, _completion_loader internally calls __load_completion
|
||||||
|
# and if it returns a non-zero status, it sets the default 'minimal' completion.
|
||||||
|
_fzf_completion_loader=__load_completion
|
||||||
|
elif type _completion_loader > /dev/null 2>&1; then
|
||||||
|
_fzf_completion_loader=_completion_loader
|
||||||
fi
|
fi
|
||||||
|
|
||||||
__fzf_defc() {
|
__fzf_defc() {
|
||||||
@@ -539,7 +548,7 @@ done
|
|||||||
|
|
||||||
# Directory
|
# Directory
|
||||||
for cmd in $d_cmds; do
|
for cmd in $d_cmds; do
|
||||||
__fzf_defc "$cmd" _fzf_dir_completion "-o nospace -o dirnames"
|
__fzf_defc "$cmd" _fzf_dir_completion "-o bashdefault -o nospace -o dirnames"
|
||||||
done
|
done
|
||||||
|
|
||||||
# ssh
|
# ssh
|
||||||
@@ -572,3 +581,5 @@ _fzf_setup_completion 'var' export unset printenv
|
|||||||
_fzf_setup_completion 'alias' unalias
|
_fzf_setup_completion 'alias' unalias
|
||||||
_fzf_setup_completion 'host' telnet
|
_fzf_setup_completion 'host' telnet
|
||||||
_fzf_setup_completion 'proc' kill
|
_fzf_setup_completion 'proc' kill
|
||||||
|
|
||||||
|
fi
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
# - $FZF_COMPLETION_TRIGGER (default: '**')
|
# - $FZF_COMPLETION_TRIGGER (default: '**')
|
||||||
# - $FZF_COMPLETION_OPTS (default: empty)
|
# - $FZF_COMPLETION_OPTS (default: empty)
|
||||||
|
|
||||||
[[ -o interactive ]] || return 0
|
if [[ -o interactive ]]; then
|
||||||
|
|
||||||
|
|
||||||
# Both branches of the following `if` do the same thing -- define
|
# Both branches of the following `if` do the same thing -- define
|
||||||
@@ -351,3 +351,5 @@ bindkey '^I' fzf-completion
|
|||||||
eval $__fzf_completion_options
|
eval $__fzf_completion_options
|
||||||
'unset' '__fzf_completion_options'
|
'unset' '__fzf_completion_options'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fi
|
||||||
|
|||||||
+19
-11
@@ -11,7 +11,7 @@
|
|||||||
# - $FZF_ALT_C_COMMAND
|
# - $FZF_ALT_C_COMMAND
|
||||||
# - $FZF_ALT_C_OPTS
|
# - $FZF_ALT_C_OPTS
|
||||||
|
|
||||||
[[ $- =~ i ]] || return 0
|
if [[ $- =~ i ]]; then
|
||||||
|
|
||||||
|
|
||||||
# Key bindings
|
# Key bindings
|
||||||
@@ -41,7 +41,7 @@ __fzf_cd__() {
|
|||||||
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse --walker=dir,follow,hidden --scheme=path ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-} +m"
|
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse --walker=dir,follow,hidden --scheme=path ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-} +m"
|
||||||
dir=$(
|
dir=$(
|
||||||
FZF_DEFAULT_COMMAND=${FZF_ALT_C_COMMAND:-} FZF_DEFAULT_OPTS="$opts" $(__fzfcmd)
|
FZF_DEFAULT_COMMAND=${FZF_ALT_C_COMMAND:-} FZF_DEFAULT_OPTS="$opts" $(__fzfcmd)
|
||||||
) && printf 'builtin cd -- %q' "$dir"
|
) && printf 'builtin cd -- %q' "$(builtin unset CDPATH && builtin cd -- "$dir" && builtin pwd)"
|
||||||
}
|
}
|
||||||
|
|
||||||
if command -v perl > /dev/null; then
|
if command -v perl > /dev/null; then
|
||||||
@@ -102,9 +102,11 @@ bind -m emacs-standard '"\C-z": vi-editing-mode'
|
|||||||
|
|
||||||
if (( BASH_VERSINFO[0] < 4 )); then
|
if (( BASH_VERSINFO[0] < 4 )); then
|
||||||
# CTRL-T - Paste the selected file path into the command line
|
# CTRL-T - Paste the selected file path into the command line
|
||||||
bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"'
|
if [[ "${FZF_CTRL_T_COMMAND-x}" != "" ]]; then
|
||||||
bind -m vi-command '"\C-t": "\C-z\C-t\C-z"'
|
bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"'
|
||||||
bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"'
|
bind -m vi-command '"\C-t": "\C-z\C-t\C-z"'
|
||||||
|
bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"'
|
||||||
|
fi
|
||||||
|
|
||||||
# CTRL-R - Paste the selected command from history into the command line
|
# CTRL-R - Paste the selected command from history into the command line
|
||||||
bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u`__fzf_history__`\e\C-e\er"'
|
bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u`__fzf_history__`\e\C-e\er"'
|
||||||
@@ -112,9 +114,11 @@ if (( BASH_VERSINFO[0] < 4 )); then
|
|||||||
bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
|
bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
|
||||||
else
|
else
|
||||||
# CTRL-T - Paste the selected file path into the command line
|
# CTRL-T - Paste the selected file path into the command line
|
||||||
bind -m emacs-standard -x '"\C-t": fzf-file-widget'
|
if [[ "${FZF_CTRL_T_COMMAND-x}" != "" ]]; then
|
||||||
bind -m vi-command -x '"\C-t": fzf-file-widget'
|
bind -m emacs-standard -x '"\C-t": fzf-file-widget'
|
||||||
bind -m vi-insert -x '"\C-t": fzf-file-widget'
|
bind -m vi-command -x '"\C-t": fzf-file-widget'
|
||||||
|
bind -m vi-insert -x '"\C-t": fzf-file-widget'
|
||||||
|
fi
|
||||||
|
|
||||||
# CTRL-R - Paste the selected command from history into the command line
|
# CTRL-R - Paste the selected command from history into the command line
|
||||||
bind -m emacs-standard -x '"\C-r": __fzf_history__'
|
bind -m emacs-standard -x '"\C-r": __fzf_history__'
|
||||||
@@ -123,6 +127,10 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ALT-C - cd into the selected directory
|
# ALT-C - cd into the selected directory
|
||||||
bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"'
|
if [[ "${FZF_ALT_C_COMMAND-x}" != "" ]]; then
|
||||||
bind -m vi-command '"\ec": "\C-z\ec\C-z"'
|
bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"'
|
||||||
bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
|
bind -m vi-command '"\ec": "\C-z\ec\C-z"'
|
||||||
|
bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|||||||
+16
-8
@@ -21,13 +21,13 @@ function fzf_key_bindings
|
|||||||
# Store current token in $dir as root for the 'find' command
|
# Store current token in $dir as root for the 'find' command
|
||||||
function fzf-file-widget -d "List files and folders"
|
function fzf-file-widget -d "List files and folders"
|
||||||
set -l commandline (__fzf_parse_commandline)
|
set -l commandline (__fzf_parse_commandline)
|
||||||
set -l dir $commandline[1]
|
set -lx dir $commandline[1]
|
||||||
set -l fzf_query $commandline[2]
|
set -l fzf_query $commandline[2]
|
||||||
set -l prefix $commandline[3]
|
set -l prefix $commandline[3]
|
||||||
|
|
||||||
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
||||||
begin
|
begin
|
||||||
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse --walker=file,dir,follow,hidden --scheme=path --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS"
|
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse --walker=file,dir,follow,hidden --walker-root='$dir' --scheme=path --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS"
|
||||||
set -lx FZF_DEFAULT_COMMAND "$FZF_CTRL_T_COMMAND"
|
set -lx FZF_DEFAULT_COMMAND "$FZF_CTRL_T_COMMAND"
|
||||||
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 result $result $r; end
|
||||||
end
|
end
|
||||||
@@ -70,13 +70,13 @@ function fzf_key_bindings
|
|||||||
|
|
||||||
function fzf-cd-widget -d "Change directory"
|
function fzf-cd-widget -d "Change directory"
|
||||||
set -l commandline (__fzf_parse_commandline)
|
set -l commandline (__fzf_parse_commandline)
|
||||||
set -l dir $commandline[1]
|
set -lx dir $commandline[1]
|
||||||
set -l fzf_query $commandline[2]
|
set -l fzf_query $commandline[2]
|
||||||
set -l prefix $commandline[3]
|
set -l prefix $commandline[3]
|
||||||
|
|
||||||
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
||||||
begin
|
begin
|
||||||
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse --walker=dir,follow,hidden --scheme=path --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS"
|
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse --walker=dir,follow,hidden --walker-root='$dir' --scheme=path --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS"
|
||||||
set -lx FZF_DEFAULT_COMMAND "$FZF_ALT_C_COMMAND"
|
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
|
||||||
|
|
||||||
@@ -104,14 +104,22 @@ function fzf_key_bindings
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
bind \ct fzf-file-widget
|
|
||||||
bind \cr fzf-history-widget
|
bind \cr fzf-history-widget
|
||||||
bind \ec fzf-cd-widget
|
if not set -q FZF_CTRL_T_COMMAND; or test -n "$FZF_CTRL_T_COMMAND"
|
||||||
|
bind \ct fzf-file-widget
|
||||||
|
end
|
||||||
|
if not set -q FZF_ALT_C_COMMAND; or test -n "$FZF_ALT_C_COMMAND"
|
||||||
|
bind \ec fzf-cd-widget
|
||||||
|
end
|
||||||
|
|
||||||
if bind -M insert > /dev/null 2>&1
|
if bind -M insert > /dev/null 2>&1
|
||||||
bind -M insert \ct fzf-file-widget
|
|
||||||
bind -M insert \cr fzf-history-widget
|
bind -M insert \cr fzf-history-widget
|
||||||
bind -M insert \ec fzf-cd-widget
|
if not set -q FZF_CTRL_T_COMMAND; or test -n "$FZF_CTRL_T_COMMAND"
|
||||||
|
bind -M insert \ct fzf-file-widget
|
||||||
|
end
|
||||||
|
if not set -q FZF_ALT_C_COMMAND; or test -n "$FZF_ALT_C_COMMAND"
|
||||||
|
bind -M insert \ec fzf-cd-widget
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
|
function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
|
||||||
|
|||||||
+16
-10
@@ -11,7 +11,7 @@
|
|||||||
# - $FZF_ALT_C_COMMAND
|
# - $FZF_ALT_C_COMMAND
|
||||||
# - $FZF_ALT_C_OPTS
|
# - $FZF_ALT_C_OPTS
|
||||||
|
|
||||||
[[ -o interactive ]] || return 0
|
if [[ -o interactive ]]; then
|
||||||
|
|
||||||
|
|
||||||
# Key bindings
|
# Key bindings
|
||||||
@@ -62,10 +62,12 @@ fzf-file-widget() {
|
|||||||
zle reset-prompt
|
zle reset-prompt
|
||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
zle -N fzf-file-widget
|
if [[ "${FZF_CTRL_T_COMMAND-x}" != "" ]]; then
|
||||||
bindkey -M emacs '^T' fzf-file-widget
|
zle -N fzf-file-widget
|
||||||
bindkey -M vicmd '^T' fzf-file-widget
|
bindkey -M emacs '^T' fzf-file-widget
|
||||||
bindkey -M viins '^T' fzf-file-widget
|
bindkey -M vicmd '^T' fzf-file-widget
|
||||||
|
bindkey -M viins '^T' fzf-file-widget
|
||||||
|
fi
|
||||||
|
|
||||||
# ALT-C - cd into the selected directory
|
# ALT-C - cd into the selected directory
|
||||||
fzf-cd-widget() {
|
fzf-cd-widget() {
|
||||||
@@ -76,17 +78,19 @@ fzf-cd-widget() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
zle push-line # Clear buffer. Auto-restored on next prompt.
|
zle push-line # Clear buffer. Auto-restored on next prompt.
|
||||||
BUFFER="builtin cd -- ${(q)dir}"
|
BUFFER="builtin cd -- ${(q)dir:a}"
|
||||||
zle accept-line
|
zle accept-line
|
||||||
local ret=$?
|
local ret=$?
|
||||||
unset dir # ensure this doesn't end up appearing in prompt expansion
|
unset dir # ensure this doesn't end up appearing in prompt expansion
|
||||||
zle reset-prompt
|
zle reset-prompt
|
||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
zle -N fzf-cd-widget
|
if [[ "${FZF_ALT_C_COMMAND-x}" != "" ]]; then
|
||||||
bindkey -M emacs '\ec' fzf-cd-widget
|
zle -N fzf-cd-widget
|
||||||
bindkey -M vicmd '\ec' fzf-cd-widget
|
bindkey -M emacs '\ec' fzf-cd-widget
|
||||||
bindkey -M viins '\ec' fzf-cd-widget
|
bindkey -M vicmd '\ec' fzf-cd-widget
|
||||||
|
bindkey -M viins '\ec' fzf-cd-widget
|
||||||
|
fi
|
||||||
|
|
||||||
# CTRL-R - Paste the selected command from history into the command line
|
# CTRL-R - Paste the selected command from history into the command line
|
||||||
fzf-history-widget() {
|
fzf-history-widget() {
|
||||||
@@ -115,3 +119,5 @@ bindkey -M viins '^R' fzf-history-widget
|
|||||||
eval $__fzf_key_bindings_options
|
eval $__fzf_key_bindings_options
|
||||||
'unset' '__fzf_key_bindings_options'
|
'unset' '__fzf_key_bindings_options'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fi
|
||||||
|
|||||||
+65
-63
@@ -54,72 +54,74 @@ func _() {
|
|||||||
_ = x[actToggleIn-43]
|
_ = x[actToggleIn-43]
|
||||||
_ = x[actToggleOut-44]
|
_ = x[actToggleOut-44]
|
||||||
_ = x[actToggleTrack-45]
|
_ = x[actToggleTrack-45]
|
||||||
_ = x[actToggleHeader-46]
|
_ = x[actToggleTrackCurrent-46]
|
||||||
_ = x[actTrack-47]
|
_ = x[actToggleHeader-47]
|
||||||
_ = x[actDown-48]
|
_ = x[actTrackCurrent-48]
|
||||||
_ = x[actUp-49]
|
_ = x[actUntrackCurrent-49]
|
||||||
_ = x[actPageUp-50]
|
_ = x[actDown-50]
|
||||||
_ = x[actPageDown-51]
|
_ = x[actUp-51]
|
||||||
_ = x[actPosition-52]
|
_ = x[actPageUp-52]
|
||||||
_ = x[actHalfPageUp-53]
|
_ = x[actPageDown-53]
|
||||||
_ = x[actHalfPageDown-54]
|
_ = x[actPosition-54]
|
||||||
_ = x[actOffsetUp-55]
|
_ = x[actHalfPageUp-55]
|
||||||
_ = x[actOffsetDown-56]
|
_ = x[actHalfPageDown-56]
|
||||||
_ = x[actJump-57]
|
_ = x[actOffsetUp-57]
|
||||||
_ = x[actJumpAccept-58]
|
_ = x[actOffsetDown-58]
|
||||||
_ = x[actPrintQuery-59]
|
_ = x[actJump-59]
|
||||||
_ = x[actRefreshPreview-60]
|
_ = x[actJumpAccept-60]
|
||||||
_ = x[actReplaceQuery-61]
|
_ = x[actPrintQuery-61]
|
||||||
_ = x[actToggleSort-62]
|
_ = x[actRefreshPreview-62]
|
||||||
_ = x[actShowPreview-63]
|
_ = x[actReplaceQuery-63]
|
||||||
_ = x[actHidePreview-64]
|
_ = x[actToggleSort-64]
|
||||||
_ = x[actTogglePreview-65]
|
_ = x[actShowPreview-65]
|
||||||
_ = x[actTogglePreviewWrap-66]
|
_ = x[actHidePreview-66]
|
||||||
_ = x[actTransform-67]
|
_ = x[actTogglePreview-67]
|
||||||
_ = x[actTransformBorderLabel-68]
|
_ = x[actTogglePreviewWrap-68]
|
||||||
_ = x[actTransformHeader-69]
|
_ = x[actTransform-69]
|
||||||
_ = x[actTransformPreviewLabel-70]
|
_ = x[actTransformBorderLabel-70]
|
||||||
_ = x[actTransformPrompt-71]
|
_ = x[actTransformHeader-71]
|
||||||
_ = x[actTransformQuery-72]
|
_ = x[actTransformPreviewLabel-72]
|
||||||
_ = x[actPreview-73]
|
_ = x[actTransformPrompt-73]
|
||||||
_ = x[actChangePreview-74]
|
_ = x[actTransformQuery-74]
|
||||||
_ = x[actChangePreviewWindow-75]
|
_ = x[actPreview-75]
|
||||||
_ = x[actPreviewTop-76]
|
_ = x[actChangePreview-76]
|
||||||
_ = x[actPreviewBottom-77]
|
_ = x[actChangePreviewWindow-77]
|
||||||
_ = x[actPreviewUp-78]
|
_ = x[actPreviewTop-78]
|
||||||
_ = x[actPreviewDown-79]
|
_ = x[actPreviewBottom-79]
|
||||||
_ = x[actPreviewPageUp-80]
|
_ = x[actPreviewUp-80]
|
||||||
_ = x[actPreviewPageDown-81]
|
_ = x[actPreviewDown-81]
|
||||||
_ = x[actPreviewHalfPageUp-82]
|
_ = x[actPreviewPageUp-82]
|
||||||
_ = x[actPreviewHalfPageDown-83]
|
_ = x[actPreviewPageDown-83]
|
||||||
_ = x[actPrevHistory-84]
|
_ = x[actPreviewHalfPageUp-84]
|
||||||
_ = x[actPrevSelected-85]
|
_ = x[actPreviewHalfPageDown-85]
|
||||||
_ = x[actPut-86]
|
_ = x[actPrevHistory-86]
|
||||||
_ = x[actNextHistory-87]
|
_ = x[actPrevSelected-87]
|
||||||
_ = x[actNextSelected-88]
|
_ = x[actPut-88]
|
||||||
_ = x[actExecute-89]
|
_ = x[actNextHistory-89]
|
||||||
_ = x[actExecuteSilent-90]
|
_ = x[actNextSelected-90]
|
||||||
_ = x[actExecuteMulti-91]
|
_ = x[actExecute-91]
|
||||||
_ = x[actSigStop-92]
|
_ = x[actExecuteSilent-92]
|
||||||
_ = x[actFirst-93]
|
_ = x[actExecuteMulti-93]
|
||||||
_ = x[actLast-94]
|
_ = x[actSigStop-94]
|
||||||
_ = x[actReload-95]
|
_ = x[actFirst-95]
|
||||||
_ = x[actReloadSync-96]
|
_ = x[actLast-96]
|
||||||
_ = x[actDisableSearch-97]
|
_ = x[actReload-97]
|
||||||
_ = x[actEnableSearch-98]
|
_ = x[actReloadSync-98]
|
||||||
_ = x[actSelect-99]
|
_ = x[actDisableSearch-99]
|
||||||
_ = x[actDeselect-100]
|
_ = x[actEnableSearch-100]
|
||||||
_ = x[actUnbind-101]
|
_ = x[actSelect-101]
|
||||||
_ = x[actRebind-102]
|
_ = x[actDeselect-102]
|
||||||
_ = x[actBecome-103]
|
_ = x[actUnbind-103]
|
||||||
_ = x[actResponse-104]
|
_ = x[actRebind-104]
|
||||||
_ = x[actShowHeader-105]
|
_ = x[actBecome-105]
|
||||||
_ = x[actHideHeader-106]
|
_ = x[actResponse-106]
|
||||||
|
_ = x[actShowHeader-107]
|
||||||
|
_ = x[actHideHeader-108]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleHeaderactTrackactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactResponseactShowHeaderactHideHeader"
|
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactResponseactShowHeaderactHideHeader"
|
||||||
|
|
||||||
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 242, 263, 278, 292, 306, 319, 336, 344, 357, 373, 385, 399, 413, 424, 435, 453, 470, 477, 496, 508, 522, 531, 546, 558, 571, 582, 593, 605, 619, 634, 642, 649, 654, 663, 674, 685, 698, 713, 724, 737, 744, 757, 770, 787, 802, 815, 829, 843, 859, 879, 891, 914, 932, 956, 974, 991, 1001, 1017, 1039, 1052, 1068, 1080, 1094, 1110, 1128, 1148, 1170, 1184, 1199, 1205, 1219, 1234, 1244, 1260, 1275, 1285, 1293, 1300, 1309, 1322, 1338, 1353, 1362, 1373, 1382, 1391, 1400, 1411, 1424, 1437}
|
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 242, 263, 278, 292, 306, 319, 336, 344, 357, 373, 385, 399, 413, 424, 435, 453, 470, 477, 496, 508, 522, 531, 546, 558, 571, 582, 593, 605, 619, 640, 655, 670, 687, 694, 699, 708, 719, 730, 743, 758, 769, 782, 789, 802, 815, 832, 847, 860, 874, 888, 904, 924, 936, 959, 977, 1001, 1019, 1036, 1046, 1062, 1084, 1097, 1113, 1125, 1139, 1155, 1173, 1193, 1215, 1229, 1244, 1250, 1264, 1279, 1289, 1305, 1320, 1330, 1338, 1345, 1354, 1367, 1383, 1398, 1407, 1418, 1427, 1436, 1445, 1456, 1469, 1482}
|
||||||
|
|
||||||
func (i actionType) String() string {
|
func (i actionType) String() string {
|
||||||
if i < 0 || i >= actionType(len(_actionType_index)-1) {
|
if i < 0 || i >= actionType(len(_actionType_index)-1) {
|
||||||
|
|||||||
+83
-50
@@ -153,6 +153,12 @@ var (
|
|||||||
bonusBoundaryDelimiter int16 = bonusBoundary + 1
|
bonusBoundaryDelimiter int16 = bonusBoundary + 1
|
||||||
|
|
||||||
initialCharClass charClass = charWhite
|
initialCharClass charClass = charWhite
|
||||||
|
|
||||||
|
// A minor optimization that can give 15%+ performance boost
|
||||||
|
asciiCharClasses [unicode.MaxASCII + 1]charClass
|
||||||
|
|
||||||
|
// A minor optimization that can give yet another 5% performance boost
|
||||||
|
bonusMatrix [charNumber + 1][charNumber + 1]int16
|
||||||
)
|
)
|
||||||
|
|
||||||
type charClass int
|
type charClass int
|
||||||
@@ -187,6 +193,27 @@ func Init(scheme string) bool {
|
|||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
for i := 0; i <= unicode.MaxASCII; i++ {
|
||||||
|
char := rune(i)
|
||||||
|
c := charNonWord
|
||||||
|
if char >= 'a' && char <= 'z' {
|
||||||
|
c = charLower
|
||||||
|
} else if char >= 'A' && char <= 'Z' {
|
||||||
|
c = charUpper
|
||||||
|
} else if char >= '0' && char <= '9' {
|
||||||
|
c = charNumber
|
||||||
|
} else if strings.ContainsRune(whiteChars, char) {
|
||||||
|
c = charWhite
|
||||||
|
} else if strings.ContainsRune(delimiterChars, char) {
|
||||||
|
c = charDelimiter
|
||||||
|
}
|
||||||
|
asciiCharClasses[i] = c
|
||||||
|
}
|
||||||
|
for i := 0; i <= int(charNumber); i++ {
|
||||||
|
for j := 0; j <= int(charNumber); j++ {
|
||||||
|
bonusMatrix[i][j] = bonusFor(charClass(i), charClass(j))
|
||||||
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,21 +241,6 @@ func alloc32(offset int, slab *util.Slab, size int) (int, []int32) {
|
|||||||
return offset, make([]int32, size)
|
return offset, make([]int32, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func charClassOfAscii(char rune) charClass {
|
|
||||||
if char >= 'a' && char <= 'z' {
|
|
||||||
return charLower
|
|
||||||
} else if char >= 'A' && char <= 'Z' {
|
|
||||||
return charUpper
|
|
||||||
} else if char >= '0' && char <= '9' {
|
|
||||||
return charNumber
|
|
||||||
} else if strings.ContainsRune(whiteChars, char) {
|
|
||||||
return charWhite
|
|
||||||
} else if strings.ContainsRune(delimiterChars, char) {
|
|
||||||
return charDelimiter
|
|
||||||
}
|
|
||||||
return charNonWord
|
|
||||||
}
|
|
||||||
|
|
||||||
func charClassOfNonAscii(char rune) charClass {
|
func charClassOfNonAscii(char rune) charClass {
|
||||||
if unicode.IsLower(char) {
|
if unicode.IsLower(char) {
|
||||||
return charLower
|
return charLower
|
||||||
@@ -248,7 +260,7 @@ func charClassOfNonAscii(char rune) charClass {
|
|||||||
|
|
||||||
func charClassOf(char rune) charClass {
|
func charClassOf(char rune) charClass {
|
||||||
if char <= unicode.MaxASCII {
|
if char <= unicode.MaxASCII {
|
||||||
return charClassOfAscii(char)
|
return asciiCharClasses[char]
|
||||||
}
|
}
|
||||||
return charClassOfNonAscii(char)
|
return charClassOfNonAscii(char)
|
||||||
}
|
}
|
||||||
@@ -287,7 +299,7 @@ func bonusAt(input *util.Chars, idx int) int16 {
|
|||||||
if idx == 0 {
|
if idx == 0 {
|
||||||
return bonusBoundaryWhite
|
return bonusBoundaryWhite
|
||||||
}
|
}
|
||||||
return bonusFor(charClassOf(input.Get(idx-1)), charClassOf(input.Get(idx)))
|
return bonusMatrix[charClassOf(input.Get(idx-1))][charClassOf(input.Get(idx))]
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeRune(r rune) rune {
|
func normalizeRune(r rune) rune {
|
||||||
@@ -340,30 +352,45 @@ func isAscii(runes []rune) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func asciiFuzzyIndex(input *util.Chars, pattern []rune, caseSensitive bool) int {
|
func asciiFuzzyIndex(input *util.Chars, pattern []rune, caseSensitive bool) (int, int) {
|
||||||
// Can't determine
|
// Can't determine
|
||||||
if !input.IsBytes() {
|
if !input.IsBytes() {
|
||||||
return 0
|
return 0, input.Length()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not possible
|
// Not possible
|
||||||
if !isAscii(pattern) {
|
if !isAscii(pattern) {
|
||||||
return -1
|
return -1, -1
|
||||||
}
|
}
|
||||||
|
|
||||||
firstIdx, idx := 0, 0
|
firstIdx, idx, lastIdx := 0, 0, 0
|
||||||
|
var b byte
|
||||||
for pidx := 0; pidx < len(pattern); pidx++ {
|
for pidx := 0; pidx < len(pattern); pidx++ {
|
||||||
idx = trySkip(input, caseSensitive, byte(pattern[pidx]), idx)
|
b = byte(pattern[pidx])
|
||||||
|
idx = trySkip(input, caseSensitive, b, idx)
|
||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
return -1
|
return -1, -1
|
||||||
}
|
}
|
||||||
if pidx == 0 && idx > 0 {
|
if pidx == 0 && idx > 0 {
|
||||||
// Step back to find the right bonus point
|
// Step back to find the right bonus point
|
||||||
firstIdx = idx - 1
|
firstIdx = idx - 1
|
||||||
}
|
}
|
||||||
|
lastIdx = idx
|
||||||
idx++
|
idx++
|
||||||
}
|
}
|
||||||
return firstIdx
|
|
||||||
|
// Find the last appearance of the last character of the pattern to limit the search scope
|
||||||
|
bu := b
|
||||||
|
if !caseSensitive && b >= 'a' && b <= 'z' {
|
||||||
|
bu = b - 32
|
||||||
|
}
|
||||||
|
scope := input.Bytes()[lastIdx:]
|
||||||
|
for offset := len(scope) - 1; offset > 0; offset-- {
|
||||||
|
if scope[offset] == b || scope[offset] == bu {
|
||||||
|
return firstIdx, lastIdx + offset + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firstIdx, lastIdx + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func debugV2(T []rune, pattern []rune, F []int32, lastIdx int, H []int16, C []int16) {
|
func debugV2(T []rune, pattern []rune, F []int32, lastIdx int, H []int16, C []int16) {
|
||||||
@@ -412,6 +439,9 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
|
|||||||
return Result{0, 0, 0}, posArray(withPos, M)
|
return Result{0, 0, 0}, posArray(withPos, M)
|
||||||
}
|
}
|
||||||
N := input.Length()
|
N := input.Length()
|
||||||
|
if M > N {
|
||||||
|
return Result{-1, -1, 0}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Since O(nm) algorithm can be prohibitively expensive for large input,
|
// Since O(nm) algorithm can be prohibitively expensive for large input,
|
||||||
// we fall back to the greedy algorithm.
|
// we fall back to the greedy algorithm.
|
||||||
@@ -420,10 +450,12 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Phase 1. Optimized search for ASCII string
|
// Phase 1. Optimized search for ASCII string
|
||||||
idx := asciiFuzzyIndex(input, pattern, caseSensitive)
|
minIdx, maxIdx := asciiFuzzyIndex(input, pattern, caseSensitive)
|
||||||
if idx < 0 {
|
if minIdx < 0 {
|
||||||
return Result{-1, -1, 0}, nil
|
return Result{-1, -1, 0}, nil
|
||||||
}
|
}
|
||||||
|
// fmt.Println(N, maxIdx, idx, maxIdx-idx, input.ToString())
|
||||||
|
N = maxIdx - minIdx
|
||||||
|
|
||||||
// Reuse pre-allocated integer slice to avoid unnecessary sweeping of garbages
|
// Reuse pre-allocated integer slice to avoid unnecessary sweeping of garbages
|
||||||
offset16 := 0
|
offset16 := 0
|
||||||
@@ -436,20 +468,19 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
|
|||||||
offset32, F := alloc32(offset32, slab, M)
|
offset32, F := alloc32(offset32, slab, M)
|
||||||
// Rune array
|
// Rune array
|
||||||
_, T := alloc32(offset32, slab, N)
|
_, T := alloc32(offset32, slab, N)
|
||||||
input.CopyRunes(T)
|
input.CopyRunes(T, minIdx)
|
||||||
|
|
||||||
// Phase 2. Calculate bonus for each point
|
// Phase 2. Calculate bonus for each point
|
||||||
maxScore, maxScorePos := int16(0), 0
|
maxScore, maxScorePos := int16(0), 0
|
||||||
pidx, lastIdx := 0, 0
|
pidx, lastIdx := 0, 0
|
||||||
pchar0, pchar, prevH0, prevClass, inGap := pattern[0], pattern[0], int16(0), initialCharClass, false
|
pchar0, pchar, prevH0, prevClass, inGap := pattern[0], pattern[0], int16(0), initialCharClass, false
|
||||||
Tsub := T[idx:]
|
for off, char := range T {
|
||||||
H0sub, C0sub, Bsub := H0[idx:][:len(Tsub)], C0[idx:][:len(Tsub)], B[idx:][:len(Tsub)]
|
|
||||||
for off, char := range Tsub {
|
|
||||||
var class charClass
|
var class charClass
|
||||||
if char <= unicode.MaxASCII {
|
if char <= unicode.MaxASCII {
|
||||||
class = charClassOfAscii(char)
|
class = asciiCharClasses[char]
|
||||||
if !caseSensitive && class == charUpper {
|
if !caseSensitive && class == charUpper {
|
||||||
char += 32
|
char += 32
|
||||||
|
T[off] = char
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
class = charClassOfNonAscii(char)
|
class = charClassOfNonAscii(char)
|
||||||
@@ -459,28 +490,28 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
|
|||||||
if normalize {
|
if normalize {
|
||||||
char = normalizeRune(char)
|
char = normalizeRune(char)
|
||||||
}
|
}
|
||||||
|
T[off] = char
|
||||||
}
|
}
|
||||||
|
|
||||||
Tsub[off] = char
|
bonus := bonusMatrix[prevClass][class]
|
||||||
bonus := bonusFor(prevClass, class)
|
B[off] = bonus
|
||||||
Bsub[off] = bonus
|
|
||||||
prevClass = class
|
prevClass = class
|
||||||
|
|
||||||
if char == pchar {
|
if char == pchar {
|
||||||
if pidx < M {
|
if pidx < M {
|
||||||
F[pidx] = int32(idx + off)
|
F[pidx] = int32(off)
|
||||||
pidx++
|
pidx++
|
||||||
pchar = pattern[util.Min(pidx, M-1)]
|
pchar = pattern[util.Min(pidx, M-1)]
|
||||||
}
|
}
|
||||||
lastIdx = idx + off
|
lastIdx = off
|
||||||
}
|
}
|
||||||
|
|
||||||
if char == pchar0 {
|
if char == pchar0 {
|
||||||
score := scoreMatch + bonus*bonusFirstCharMultiplier
|
score := scoreMatch + bonus*bonusFirstCharMultiplier
|
||||||
H0sub[off] = score
|
H0[off] = score
|
||||||
C0sub[off] = 1
|
C0[off] = 1
|
||||||
if M == 1 && (forward && score > maxScore || !forward && score >= maxScore) {
|
if M == 1 && (forward && score > maxScore || !forward && score >= maxScore) {
|
||||||
maxScore, maxScorePos = score, idx+off
|
maxScore, maxScorePos = score, off
|
||||||
if forward && bonus >= bonusBoundary {
|
if forward && bonus >= bonusBoundary {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -488,24 +519,24 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
|
|||||||
inGap = false
|
inGap = false
|
||||||
} else {
|
} else {
|
||||||
if inGap {
|
if inGap {
|
||||||
H0sub[off] = util.Max16(prevH0+scoreGapExtension, 0)
|
H0[off] = util.Max16(prevH0+scoreGapExtension, 0)
|
||||||
} else {
|
} else {
|
||||||
H0sub[off] = util.Max16(prevH0+scoreGapStart, 0)
|
H0[off] = util.Max16(prevH0+scoreGapStart, 0)
|
||||||
}
|
}
|
||||||
C0sub[off] = 0
|
C0[off] = 0
|
||||||
inGap = true
|
inGap = true
|
||||||
}
|
}
|
||||||
prevH0 = H0sub[off]
|
prevH0 = H0[off]
|
||||||
}
|
}
|
||||||
if pidx != M {
|
if pidx != M {
|
||||||
return Result{-1, -1, 0}, nil
|
return Result{-1, -1, 0}, nil
|
||||||
}
|
}
|
||||||
if M == 1 {
|
if M == 1 {
|
||||||
result := Result{maxScorePos, maxScorePos + 1, int(maxScore)}
|
result := Result{minIdx + maxScorePos, minIdx + maxScorePos + 1, int(maxScore)}
|
||||||
if !withPos {
|
if !withPos {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
pos := []int{maxScorePos}
|
pos := []int{minIdx + maxScorePos}
|
||||||
return result, &pos
|
return result, &pos
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,7 +633,7 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s > s1 && (s > s2 || s == s2 && preferMatch) {
|
if s > s1 && (s > s2 || s == s2 && preferMatch) {
|
||||||
*pos = append(*pos, j)
|
*pos = append(*pos, j+minIdx)
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -615,7 +646,7 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
|
|||||||
// Start offset we return here is only relevant when begin tiebreak is used.
|
// Start offset we return here is only relevant when begin tiebreak is used.
|
||||||
// However finding the accurate offset requires backtracking, and we don't
|
// However finding the accurate offset requires backtracking, and we don't
|
||||||
// want to pay extra cost for the option that has lost its importance.
|
// want to pay extra cost for the option that has lost its importance.
|
||||||
return Result{j, maxScorePos + 1, int(maxScore)}, pos
|
return Result{minIdx + j, minIdx + maxScorePos + 1, int(maxScore)}, pos
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement the same sorting criteria as V2
|
// Implement the same sorting criteria as V2
|
||||||
@@ -645,7 +676,7 @@ func calculateScore(caseSensitive bool, normalize bool, text *util.Chars, patter
|
|||||||
*pos = append(*pos, idx)
|
*pos = append(*pos, idx)
|
||||||
}
|
}
|
||||||
score += scoreMatch
|
score += scoreMatch
|
||||||
bonus := bonusFor(prevClass, class)
|
bonus := bonusMatrix[prevClass][class]
|
||||||
if consecutive == 0 {
|
if consecutive == 0 {
|
||||||
firstBonus = bonus
|
firstBonus = bonus
|
||||||
} else {
|
} else {
|
||||||
@@ -683,7 +714,8 @@ func FuzzyMatchV1(caseSensitive bool, normalize bool, forward bool, text *util.C
|
|||||||
if len(pattern) == 0 {
|
if len(pattern) == 0 {
|
||||||
return Result{0, 0, 0}, nil
|
return Result{0, 0, 0}, nil
|
||||||
}
|
}
|
||||||
if asciiFuzzyIndex(text, pattern, caseSensitive) < 0 {
|
idx, _ := asciiFuzzyIndex(text, pattern, caseSensitive)
|
||||||
|
if idx < 0 {
|
||||||
return Result{-1, -1, 0}, nil
|
return Result{-1, -1, 0}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -777,7 +809,8 @@ func ExactMatchNaive(caseSensitive bool, normalize bool, forward bool, text *uti
|
|||||||
return Result{-1, -1, 0}, nil
|
return Result{-1, -1, 0}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if asciiFuzzyIndex(text, pattern, caseSensitive) < 0 {
|
idx, _ := asciiFuzzyIndex(text, pattern, caseSensitive)
|
||||||
|
if idx < 0 {
|
||||||
return Result{-1, -1, 0}, nil
|
return Result{-1, -1, 0}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import (
|
|||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Init("default")
|
||||||
|
}
|
||||||
|
|
||||||
func assertMatch(t *testing.T, fun Algo, caseSensitive, forward bool, input, pattern string, sidx int, eidx int, score int) {
|
func assertMatch(t *testing.T, fun Algo, caseSensitive, forward bool, input, pattern string, sidx int, eidx int, score int) {
|
||||||
assertMatch2(t, fun, caseSensitive, false, forward, input, pattern, sidx, eidx, score)
|
assertMatch2(t, fun, caseSensitive, false, forward, input, pattern, sidx, eidx, score)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -312,7 +312,7 @@ func parseAnsiCode(s string, delimiter byte) (int, byte, string) {
|
|||||||
// Inlined version of strconv.Atoi() that only handles positive
|
// Inlined version of strconv.Atoi() that only handles positive
|
||||||
// integers and does not allocate on error.
|
// integers and does not allocate on error.
|
||||||
code := 0
|
code := 0
|
||||||
for _, ch := range []byte(s) {
|
for _, ch := range sbytes(s) {
|
||||||
ch -= '0'
|
ch -= '0'
|
||||||
if ch > 9 {
|
if ch > 9 {
|
||||||
return -1, delimiter, remaining
|
return -1, delimiter, remaining
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const (
|
|||||||
|
|
||||||
// Reader
|
// Reader
|
||||||
readerBufferSize = 64 * 1024
|
readerBufferSize = 64 * 1024
|
||||||
|
readerSlabSize = 128 * 1024
|
||||||
readerPollIntervalMin = 10 * time.Millisecond
|
readerPollIntervalMin = 10 * time.Millisecond
|
||||||
readerPollIntervalStep = 5 * time.Millisecond
|
readerPollIntervalStep = 5 * time.Millisecond
|
||||||
readerPollIntervalMax = 50 * time.Millisecond
|
readerPollIntervalMax = 50 * time.Millisecond
|
||||||
|
|||||||
+37
-20
@@ -3,8 +3,9 @@ package fzf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
)
|
)
|
||||||
@@ -18,8 +19,18 @@ Matcher -> EvtSearchFin -> Terminal (update list)
|
|||||||
Matcher -> EvtHeader -> Terminal (update header)
|
Matcher -> EvtHeader -> Terminal (update header)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
func ustring(data []byte) string {
|
||||||
|
return unsafe.String(unsafe.SliceData(data), len(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func sbytes(data string) []byte {
|
||||||
|
return unsafe.Slice(unsafe.StringData(data), len(data))
|
||||||
|
}
|
||||||
|
|
||||||
// Run starts fzf
|
// Run starts fzf
|
||||||
func Run(opts *Options, version string, revision string) {
|
func Run(opts *Options, version string, revision string) {
|
||||||
|
defer util.RunAtExitFuncs()
|
||||||
|
|
||||||
sort := opts.Sort > 0
|
sort := opts.Sort > 0
|
||||||
sortCriteria = opts.Criteria
|
sortCriteria = opts.Criteria
|
||||||
|
|
||||||
@@ -29,7 +40,7 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
} else {
|
} else {
|
||||||
fmt.Println(version)
|
fmt.Println(version)
|
||||||
}
|
}
|
||||||
os.Exit(exitOk)
|
util.Exit(exitOk)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event channel
|
// Event channel
|
||||||
@@ -45,16 +56,16 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
if opts.Theme.Colored {
|
if opts.Theme.Colored {
|
||||||
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
||||||
prevLineAnsiState = lineAnsiState
|
prevLineAnsiState = lineAnsiState
|
||||||
trimmed, offsets, newState := extractColor(string(data), lineAnsiState, nil)
|
trimmed, offsets, newState := extractColor(ustring(data), lineAnsiState, nil)
|
||||||
lineAnsiState = newState
|
lineAnsiState = newState
|
||||||
return util.ToChars([]byte(trimmed)), offsets
|
return util.ToChars(sbytes(trimmed)), offsets
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// When color is disabled but ansi option is given,
|
// When color is disabled but ansi option is given,
|
||||||
// we simply strip out ANSI codes from the input
|
// we simply strip out ANSI codes from the input
|
||||||
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
||||||
trimmed, _, _ := extractColor(string(data), nil, nil)
|
trimmed, _, _ := extractColor(ustring(data), nil, nil)
|
||||||
return util.ToChars([]byte(trimmed)), nil
|
return util.ToChars(sbytes(trimmed)), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +77,7 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
if len(opts.WithNth) == 0 {
|
if len(opts.WithNth) == 0 {
|
||||||
chunkList = NewChunkList(func(item *Item, data []byte) bool {
|
chunkList = NewChunkList(func(item *Item, data []byte) bool {
|
||||||
if len(header) < opts.HeaderLines {
|
if len(header) < opts.HeaderLines {
|
||||||
header = append(header, string(data))
|
header = append(header, ustring(data))
|
||||||
eventBox.Set(EvtHeader, header)
|
eventBox.Set(EvtHeader, header)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -77,7 +88,7 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
chunkList = NewChunkList(func(item *Item, data []byte) bool {
|
chunkList = NewChunkList(func(item *Item, data []byte) bool {
|
||||||
tokens := Tokenize(string(data), opts.Delimiter)
|
tokens := Tokenize(ustring(data), opts.Delimiter)
|
||||||
if opts.Ansi && opts.Theme.Colored && len(tokens) > 1 {
|
if opts.Ansi && opts.Theme.Colored && len(tokens) > 1 {
|
||||||
var ansiState *ansiState
|
var ansiState *ansiState
|
||||||
if prevLineAnsiState != nil {
|
if prevLineAnsiState != nil {
|
||||||
@@ -101,7 +112,7 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
eventBox.Set(EvtHeader, header)
|
eventBox.Set(EvtHeader, header)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
item.text, item.colors = ansiProcessor([]byte(transformed))
|
item.text, item.colors = ansiProcessor(sbytes(transformed))
|
||||||
item.text.TrimTrailingWhitespaces()
|
item.text.TrimTrailingWhitespaces()
|
||||||
item.text.Index = itemIndex
|
item.text.Index = itemIndex
|
||||||
item.origText = &data
|
item.origText = &data
|
||||||
@@ -154,14 +165,17 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
found := false
|
found := false
|
||||||
if streamingFilter {
|
if streamingFilter {
|
||||||
slab := util.MakeSlab(slab16Size, slab32Size)
|
slab := util.MakeSlab(slab16Size, slab32Size)
|
||||||
|
mutex := sync.Mutex{}
|
||||||
reader := NewReader(
|
reader := NewReader(
|
||||||
func(runes []byte) bool {
|
func(runes []byte) bool {
|
||||||
item := Item{}
|
item := Item{}
|
||||||
if chunkList.trans(&item, runes) {
|
if chunkList.trans(&item, runes) {
|
||||||
|
mutex.Lock()
|
||||||
if result, _, _ := pattern.MatchItem(&item, false, slab); result != nil {
|
if result, _, _ := pattern.MatchItem(&item, false, slab); result != nil {
|
||||||
opts.Printer(item.text.ToString())
|
opts.Printer(item.text.ToString())
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
|
mutex.Unlock()
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}, eventBox, opts.ReadZero, false)
|
}, eventBox, opts.ReadZero, false)
|
||||||
@@ -180,9 +194,9 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if found {
|
if found {
|
||||||
os.Exit(exitOk)
|
util.Exit(exitOk)
|
||||||
}
|
}
|
||||||
os.Exit(exitNoMatch)
|
util.Exit(exitNoMatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synchronous search
|
// Synchronous search
|
||||||
@@ -245,11 +259,8 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
delay := true
|
delay := true
|
||||||
ticks++
|
ticks++
|
||||||
input := func() []rune {
|
input := func() []rune {
|
||||||
reloaded := snapshotRevision != inputRevision
|
|
||||||
paused, input := terminal.Input()
|
paused, input := terminal.Input()
|
||||||
if reloaded && paused {
|
if !paused {
|
||||||
query = []rune{}
|
|
||||||
} else if !paused {
|
|
||||||
query = input
|
query = input
|
||||||
}
|
}
|
||||||
return query
|
return query
|
||||||
@@ -264,7 +275,7 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
if reading {
|
if reading {
|
||||||
reader.terminate()
|
reader.terminate()
|
||||||
}
|
}
|
||||||
os.Exit(value.(int))
|
util.Exit(value.(int))
|
||||||
case EvtReadNew, EvtReadFin:
|
case EvtReadNew, EvtReadFin:
|
||||||
if evt == EvtReadFin && nextCommand != nil {
|
if evt == EvtReadFin && nextCommand != nil {
|
||||||
restart(*nextCommand, nextEnviron)
|
restart(*nextCommand, nextEnviron)
|
||||||
@@ -278,6 +289,9 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
useSnapshot = false
|
useSnapshot = false
|
||||||
}
|
}
|
||||||
if !useSnapshot {
|
if !useSnapshot {
|
||||||
|
if snapshotRevision != inputRevision {
|
||||||
|
query = []rune{}
|
||||||
|
}
|
||||||
snapshot, count = chunkList.Snapshot()
|
snapshot, count = chunkList.Snapshot()
|
||||||
snapshotRevision = inputRevision
|
snapshotRevision = inputRevision
|
||||||
}
|
}
|
||||||
@@ -319,10 +333,13 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if !useSnapshot {
|
if !useSnapshot {
|
||||||
newSnapshot, _ := chunkList.Snapshot()
|
newSnapshot, newCount := chunkList.Snapshot()
|
||||||
// We want to avoid showing empty list when reload is triggered
|
// We want to avoid showing empty list when reload is triggered
|
||||||
// and the query string is changed at the same time i.e. command != nil && changed
|
// and the query string is changed at the same time i.e. command != nil && changed
|
||||||
if command == nil || len(newSnapshot) > 0 {
|
if command == nil || newCount > 0 {
|
||||||
|
if snapshotRevision != inputRevision {
|
||||||
|
query = []rune{}
|
||||||
|
}
|
||||||
snapshot = newSnapshot
|
snapshot = newSnapshot
|
||||||
snapshotRevision = inputRevision
|
snapshotRevision = inputRevision
|
||||||
}
|
}
|
||||||
@@ -360,9 +377,9 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
opts.Printer(val.Get(i).item.AsString(opts.Ansi))
|
opts.Printer(val.Get(i).item.AsString(opts.Ansi))
|
||||||
}
|
}
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
os.Exit(exitOk)
|
util.Exit(exitOk)
|
||||||
}
|
}
|
||||||
os.Exit(exitNoMatch)
|
util.Exit(exitNoMatch)
|
||||||
}
|
}
|
||||||
determine(val.final)
|
determine(val.final)
|
||||||
}
|
}
|
||||||
|
|||||||
+68
-42
@@ -52,7 +52,7 @@ const usage = `usage: fzf [options]
|
|||||||
--hscroll-off=COLS Number of screen columns to keep to the right of the
|
--hscroll-off=COLS Number of screen columns to keep to the right of the
|
||||||
highlighted substring (default: 10)
|
highlighted substring (default: 10)
|
||||||
--filepath-word Make word-wise movements respect path separators
|
--filepath-word Make word-wise movements respect path separators
|
||||||
--jump-labels=CHARS Label characters for jump and jump-accept
|
--jump-labels=CHARS Label characters for jump mode
|
||||||
|
|
||||||
Layout
|
Layout
|
||||||
--height=[~]HEIGHT[%] Display fzf window below the cursor with the given
|
--height=[~]HEIGHT[%] Display fzf window below the cursor with the given
|
||||||
@@ -75,7 +75,7 @@ const usage = `usage: fzf [options]
|
|||||||
--margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
--margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
||||||
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
||||||
--info=STYLE Finder info style
|
--info=STYLE Finder info style
|
||||||
[default|right|hidden|inline[:SEPARATOR]|inline-right]
|
[default|right|hidden|inline[-right][:PREFIX]]
|
||||||
--separator=STR String to form horizontal separator on info line
|
--separator=STR String to form horizontal separator on info line
|
||||||
--no-separator Hide info line separator
|
--no-separator Hide info line separator
|
||||||
--scrollbar[=C1[C2]] Scrollbar character(s) (each for main and preview window)
|
--scrollbar[=C1[C2]] Scrollbar character(s) (each for main and preview window)
|
||||||
@@ -143,7 +143,7 @@ const usage = `usage: fzf [options]
|
|||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const defaultInfoSep = " < "
|
const defaultInfoPrefix = " < "
|
||||||
|
|
||||||
// Case denotes case-sensitivity of search
|
// Case denotes case-sensitivity of search
|
||||||
type Case int
|
type Case int
|
||||||
@@ -217,10 +217,6 @@ const (
|
|||||||
infoHidden
|
infoHidden
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s infoStyle) noExtraLine() bool {
|
|
||||||
return s == infoInline || s == infoInlineRight || s == infoHidden
|
|
||||||
}
|
|
||||||
|
|
||||||
type labelOpts struct {
|
type labelOpts struct {
|
||||||
label string
|
label string
|
||||||
column int
|
column int
|
||||||
@@ -327,7 +323,7 @@ type Options struct {
|
|||||||
ScrollOff int
|
ScrollOff int
|
||||||
FileWord bool
|
FileWord bool
|
||||||
InfoStyle infoStyle
|
InfoStyle infoStyle
|
||||||
InfoSep string
|
InfoPrefix string
|
||||||
Separator *string
|
Separator *string
|
||||||
JumpLabels string
|
JumpLabels string
|
||||||
Prompt string
|
Prompt string
|
||||||
@@ -367,6 +363,10 @@ type Options struct {
|
|||||||
WalkerRoot string
|
WalkerRoot string
|
||||||
WalkerSkip []string
|
WalkerSkip []string
|
||||||
Version bool
|
Version bool
|
||||||
|
CPUProfile string
|
||||||
|
MEMProfile string
|
||||||
|
BlockProfile string
|
||||||
|
MutexProfile string
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterNonEmpty(input []string) []string {
|
func filterNonEmpty(input []string) []string {
|
||||||
@@ -458,14 +458,14 @@ func defaultOptions() *Options {
|
|||||||
|
|
||||||
func help(code int) {
|
func help(code int) {
|
||||||
os.Stdout.WriteString(usage)
|
os.Stdout.WriteString(usage)
|
||||||
os.Exit(code)
|
util.Exit(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorContext = ""
|
var errorContext = ""
|
||||||
|
|
||||||
func errorExit(msg string) {
|
func errorExit(msg string) {
|
||||||
os.Stderr.WriteString(errorContext + msg + "\n")
|
os.Stderr.WriteString(errorContext + msg + "\n")
|
||||||
os.Exit(exitError)
|
util.Exit(exitError)
|
||||||
}
|
}
|
||||||
|
|
||||||
func optString(arg string, prefixes ...string) (bool, string) {
|
func optString(arg string, prefixes ...string) (bool, string) {
|
||||||
@@ -670,8 +670,8 @@ func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.E
|
|||||||
add(tui.CtrlM)
|
add(tui.CtrlM)
|
||||||
case "space":
|
case "space":
|
||||||
chords[tui.Key(' ')] = key
|
chords[tui.Key(' ')] = key
|
||||||
case "bspace", "bs":
|
case "backspace", "bspace", "bs":
|
||||||
add(tui.BSpace)
|
add(tui.Backspace)
|
||||||
case "ctrl-space":
|
case "ctrl-space":
|
||||||
add(tui.CtrlSpace)
|
add(tui.CtrlSpace)
|
||||||
case "ctrl-delete":
|
case "ctrl-delete":
|
||||||
@@ -702,12 +702,16 @@ func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.E
|
|||||||
add(tui.One)
|
add(tui.One)
|
||||||
case "zero":
|
case "zero":
|
||||||
add(tui.Zero)
|
add(tui.Zero)
|
||||||
|
case "jump":
|
||||||
|
add(tui.Jump)
|
||||||
|
case "jump-cancel":
|
||||||
|
add(tui.JumpCancel)
|
||||||
case "alt-enter", "alt-return":
|
case "alt-enter", "alt-return":
|
||||||
chords[tui.CtrlAltKey('m')] = key
|
chords[tui.CtrlAltKey('m')] = key
|
||||||
case "alt-space":
|
case "alt-space":
|
||||||
chords[tui.AltKey(' ')] = key
|
chords[tui.AltKey(' ')] = key
|
||||||
case "alt-bs", "alt-bspace":
|
case "alt-bs", "alt-bspace", "alt-backspace":
|
||||||
add(tui.AltBS)
|
add(tui.AltBackspace)
|
||||||
case "alt-up":
|
case "alt-up":
|
||||||
add(tui.AltUp)
|
add(tui.AltUp)
|
||||||
case "alt-down":
|
case "alt-down":
|
||||||
@@ -719,11 +723,11 @@ func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.E
|
|||||||
case "tab":
|
case "tab":
|
||||||
add(tui.Tab)
|
add(tui.Tab)
|
||||||
case "btab", "shift-tab":
|
case "btab", "shift-tab":
|
||||||
add(tui.BTab)
|
add(tui.ShiftTab)
|
||||||
case "esc":
|
case "esc":
|
||||||
add(tui.ESC)
|
add(tui.Esc)
|
||||||
case "del":
|
case "delete", "del":
|
||||||
add(tui.Del)
|
add(tui.Delete)
|
||||||
case "home":
|
case "home":
|
||||||
add(tui.Home)
|
add(tui.Home)
|
||||||
case "end":
|
case "end":
|
||||||
@@ -731,27 +735,27 @@ func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.E
|
|||||||
case "insert":
|
case "insert":
|
||||||
add(tui.Insert)
|
add(tui.Insert)
|
||||||
case "pgup", "page-up":
|
case "pgup", "page-up":
|
||||||
add(tui.PgUp)
|
add(tui.PageUp)
|
||||||
case "pgdn", "page-down":
|
case "pgdn", "page-down":
|
||||||
add(tui.PgDn)
|
add(tui.PageDown)
|
||||||
case "alt-shift-up", "shift-alt-up":
|
case "alt-shift-up", "shift-alt-up":
|
||||||
add(tui.AltSUp)
|
add(tui.AltShiftUp)
|
||||||
case "alt-shift-down", "shift-alt-down":
|
case "alt-shift-down", "shift-alt-down":
|
||||||
add(tui.AltSDown)
|
add(tui.AltShiftDown)
|
||||||
case "alt-shift-left", "shift-alt-left":
|
case "alt-shift-left", "shift-alt-left":
|
||||||
add(tui.AltSLeft)
|
add(tui.AltShiftLeft)
|
||||||
case "alt-shift-right", "shift-alt-right":
|
case "alt-shift-right", "shift-alt-right":
|
||||||
add(tui.AltSRight)
|
add(tui.AltShiftRight)
|
||||||
case "shift-up":
|
case "shift-up":
|
||||||
add(tui.SUp)
|
add(tui.ShiftUp)
|
||||||
case "shift-down":
|
case "shift-down":
|
||||||
add(tui.SDown)
|
add(tui.ShiftDown)
|
||||||
case "shift-left":
|
case "shift-left":
|
||||||
add(tui.SLeft)
|
add(tui.ShiftLeft)
|
||||||
case "shift-right":
|
case "shift-right":
|
||||||
add(tui.SRight)
|
add(tui.ShiftRight)
|
||||||
case "shift-delete":
|
case "shift-delete":
|
||||||
add(tui.SDelete)
|
add(tui.ShiftDelete)
|
||||||
case "left-click":
|
case "left-click":
|
||||||
add(tui.LeftClick)
|
add(tui.LeftClick)
|
||||||
case "right-click":
|
case "right-click":
|
||||||
@@ -1211,14 +1215,18 @@ func parseActionList(masked string, original string, prevActions []*action, putA
|
|||||||
appendAction(actToggleSearch)
|
appendAction(actToggleSearch)
|
||||||
case "toggle-track":
|
case "toggle-track":
|
||||||
appendAction(actToggleTrack)
|
appendAction(actToggleTrack)
|
||||||
|
case "toggle-track-current":
|
||||||
|
appendAction(actToggleTrackCurrent)
|
||||||
case "toggle-header":
|
case "toggle-header":
|
||||||
appendAction(actToggleHeader)
|
appendAction(actToggleHeader)
|
||||||
case "show-header":
|
case "show-header":
|
||||||
appendAction(actShowHeader)
|
appendAction(actShowHeader)
|
||||||
case "hide-header":
|
case "hide-header":
|
||||||
appendAction(actHideHeader)
|
appendAction(actHideHeader)
|
||||||
case "track":
|
case "track", "track-current":
|
||||||
appendAction(actTrack)
|
appendAction(actTrackCurrent)
|
||||||
|
case "untrack-current":
|
||||||
|
appendAction(actUntrackCurrent)
|
||||||
case "select":
|
case "select":
|
||||||
appendAction(actSelect)
|
appendAction(actSelect)
|
||||||
case "select-all":
|
case "select-all":
|
||||||
@@ -1502,17 +1510,24 @@ func parseInfoStyle(str string) (infoStyle, string) {
|
|||||||
case "right":
|
case "right":
|
||||||
return infoRight, ""
|
return infoRight, ""
|
||||||
case "inline":
|
case "inline":
|
||||||
return infoInline, defaultInfoSep
|
return infoInline, defaultInfoPrefix
|
||||||
case "inline-right":
|
case "inline-right":
|
||||||
return infoInlineRight, ""
|
return infoInlineRight, ""
|
||||||
case "hidden":
|
case "hidden":
|
||||||
return infoHidden, ""
|
return infoHidden, ""
|
||||||
default:
|
default:
|
||||||
prefix := "inline:"
|
type infoSpec struct {
|
||||||
if strings.HasPrefix(str, prefix) {
|
name string
|
||||||
return infoInline, strings.ReplaceAll(str[len(prefix):], "\n", " ")
|
style infoStyle
|
||||||
}
|
}
|
||||||
errorExit("invalid info style (expected: default|right|hidden|inline[:SEPARATOR]|inline-right)")
|
for _, spec := range []infoSpec{
|
||||||
|
{"inline", infoInline},
|
||||||
|
{"inline-right", infoInlineRight}} {
|
||||||
|
if strings.HasPrefix(str, spec.name+":") {
|
||||||
|
return spec.style, strings.ReplaceAll(str[len(spec.name)+1:], "\n", " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errorExit("invalid info style (expected: default|right|hidden|inline[-right][:PREFIX])")
|
||||||
}
|
}
|
||||||
return infoDefault, ""
|
return infoDefault, ""
|
||||||
}
|
}
|
||||||
@@ -1803,13 +1818,13 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
case "--no-filepath-word":
|
case "--no-filepath-word":
|
||||||
opts.FileWord = false
|
opts.FileWord = false
|
||||||
case "--info":
|
case "--info":
|
||||||
opts.InfoStyle, opts.InfoSep = parseInfoStyle(
|
opts.InfoStyle, opts.InfoPrefix = parseInfoStyle(
|
||||||
nextString(allArgs, &i, "info style required"))
|
nextString(allArgs, &i, "info style required"))
|
||||||
case "--no-info":
|
case "--no-info":
|
||||||
opts.InfoStyle = infoHidden
|
opts.InfoStyle = infoHidden
|
||||||
case "--inline-info":
|
case "--inline-info":
|
||||||
opts.InfoStyle = infoInline
|
opts.InfoStyle = infoInline
|
||||||
opts.InfoSep = defaultInfoSep
|
opts.InfoPrefix = defaultInfoPrefix
|
||||||
case "--no-inline-info":
|
case "--no-inline-info":
|
||||||
opts.InfoStyle = infoDefault
|
opts.InfoStyle = infoDefault
|
||||||
case "--separator":
|
case "--separator":
|
||||||
@@ -1967,6 +1982,14 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.WalkerSkip = filterNonEmpty(strings.Split(nextString(allArgs, &i, "directory names to ignore required"), ","))
|
opts.WalkerSkip = filterNonEmpty(strings.Split(nextString(allArgs, &i, "directory names to ignore required"), ","))
|
||||||
case "--version":
|
case "--version":
|
||||||
opts.Version = true
|
opts.Version = true
|
||||||
|
case "--profile-cpu":
|
||||||
|
opts.CPUProfile = nextString(allArgs, &i, "file path required: cpu")
|
||||||
|
case "--profile-mem":
|
||||||
|
opts.MEMProfile = nextString(allArgs, &i, "file path required: mem")
|
||||||
|
case "--profile-block":
|
||||||
|
opts.BlockProfile = nextString(allArgs, &i, "file path required: block")
|
||||||
|
case "--profile-mutex":
|
||||||
|
opts.MutexProfile = nextString(allArgs, &i, "file path required: mutex")
|
||||||
case "--":
|
case "--":
|
||||||
// Ignored
|
// Ignored
|
||||||
default:
|
default:
|
||||||
@@ -2011,7 +2034,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
} else if match, value := optString(arg, "--layout="); match {
|
} else if match, value := optString(arg, "--layout="); match {
|
||||||
opts.Layout = parseLayout(value)
|
opts.Layout = parseLayout(value)
|
||||||
} else if match, value := optString(arg, "--info="); match {
|
} else if match, value := optString(arg, "--info="); match {
|
||||||
opts.InfoStyle, opts.InfoSep = parseInfoStyle(value)
|
opts.InfoStyle, opts.InfoPrefix = parseInfoStyle(value)
|
||||||
} else if match, value := optString(arg, "--separator="); match {
|
} else if match, value := optString(arg, "--separator="); match {
|
||||||
opts.Separator = &value
|
opts.Separator = &value
|
||||||
} else if match, value := optString(arg, "--scrollbar="); match {
|
} else if match, value := optString(arg, "--scrollbar="); match {
|
||||||
@@ -2236,9 +2259,7 @@ func postProcessOptions(opts *Options) {
|
|||||||
theme.Spinner = boldify(theme.Spinner)
|
theme.Spinner = boldify(theme.Spinner)
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Scheme != "default" {
|
processScheme(opts)
|
||||||
processScheme(opts)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectsArbitraryString(opt string) bool {
|
func expectsArbitraryString(opt string) bool {
|
||||||
@@ -2292,6 +2313,11 @@ func ParseOptions() *Options {
|
|||||||
errorContext = ""
|
errorContext = ""
|
||||||
parseOptions(opts, os.Args[1:])
|
parseOptions(opts, os.Args[1:])
|
||||||
|
|
||||||
|
if err := opts.initProfiling(); err != nil {
|
||||||
|
errorExit("failed to start pprof profiles: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
postProcessOptions(opts)
|
postProcessOptions(opts)
|
||||||
|
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build !pprof
|
||||||
|
// +build !pprof
|
||||||
|
|
||||||
|
package fzf
|
||||||
|
|
||||||
|
func (o *Options) initProfiling() error {
|
||||||
|
if o.CPUProfile != "" || o.MEMProfile != "" || o.BlockProfile != "" || o.MutexProfile != "" {
|
||||||
|
errorExit("error: profiling not supported: FZF must be built with '-tags=pprof' to enable profiling")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
//go:build pprof
|
||||||
|
// +build pprof
|
||||||
|
|
||||||
|
package fzf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"runtime/pprof"
|
||||||
|
|
||||||
|
"github.com/junegunn/fzf/src/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (o *Options) initProfiling() error {
|
||||||
|
if o.CPUProfile != "" {
|
||||||
|
f, err := os.Create(o.CPUProfile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not create CPU profile: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pprof.StartCPUProfile(f); err != nil {
|
||||||
|
return fmt.Errorf("could not start CPU profile: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
util.AtExit(func() {
|
||||||
|
pprof.StopCPUProfile()
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "Error: closing cpu profile:", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
stopProfile := func(name string, f *os.File) {
|
||||||
|
if err := pprof.Lookup(name).WriteTo(f, 0); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: could not write %s profile: %v\n", name, err)
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: closing %s profile: %v\n", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.MEMProfile != "" {
|
||||||
|
f, err := os.Create(o.MEMProfile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not create MEM profile: %w", err)
|
||||||
|
}
|
||||||
|
util.AtExit(func() {
|
||||||
|
runtime.GC()
|
||||||
|
stopProfile("allocs", f)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.BlockProfile != "" {
|
||||||
|
runtime.SetBlockProfileRate(1)
|
||||||
|
f, err := os.Create(o.BlockProfile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not create BLOCK profile: %w", err)
|
||||||
|
}
|
||||||
|
util.AtExit(func() { stopProfile("block", f) })
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.MutexProfile != "" {
|
||||||
|
runtime.SetMutexProfileFraction(1)
|
||||||
|
f, err := os.Create(o.MutexProfile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not create MUTEX profile: %w", err)
|
||||||
|
}
|
||||||
|
util.AtExit(func() { stopProfile("mutex", f) })
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
//go:build pprof
|
||||||
|
// +build pprof
|
||||||
|
|
||||||
|
package fzf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/junegunn/fzf/src/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// runInitProfileTests is an internal flag used TestInitProfiling
|
||||||
|
var runInitProfileTests = flag.Bool("test-init-profile", false, "run init profile tests")
|
||||||
|
|
||||||
|
func TestInitProfiling(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("short test")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run this test in a separate process since it interferes with
|
||||||
|
// profiling and modifies the global atexit state. Without this
|
||||||
|
// running `go test -bench . -cpuprofile cpu.out` will fail.
|
||||||
|
if !*runInitProfileTests {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Make sure we are not the child process.
|
||||||
|
if os.Getenv("_FZF_CHILD_PROC") != "" {
|
||||||
|
t.Fatal("already running as child process!")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(os.Args[0],
|
||||||
|
"-test.timeout", "30s",
|
||||||
|
"-test.run", "^"+t.Name()+"$",
|
||||||
|
"-test-init-profile",
|
||||||
|
)
|
||||||
|
cmd.Env = append(os.Environ(), "_FZF_CHILD_PROC=1")
|
||||||
|
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
out = bytes.TrimSpace(out)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Child test process failed: %v:\n%s", err, out)
|
||||||
|
}
|
||||||
|
// Make sure the test actually ran
|
||||||
|
if bytes.Contains(out, []byte("no tests to run")) {
|
||||||
|
t.Fatalf("Failed to run test %q:\n%s", t.Name(), out)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Child process
|
||||||
|
|
||||||
|
tempdir := t.TempDir()
|
||||||
|
t.Cleanup(util.RunAtExitFuncs)
|
||||||
|
|
||||||
|
o := Options{
|
||||||
|
CPUProfile: filepath.Join(tempdir, "cpu.prof"),
|
||||||
|
MEMProfile: filepath.Join(tempdir, "mem.prof"),
|
||||||
|
BlockProfile: filepath.Join(tempdir, "block.prof"),
|
||||||
|
MutexProfile: filepath.Join(tempdir, "mutex.prof"),
|
||||||
|
}
|
||||||
|
if err := o.initProfiling(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
profiles := []string{
|
||||||
|
o.CPUProfile,
|
||||||
|
o.MEMProfile,
|
||||||
|
o.BlockProfile,
|
||||||
|
o.MutexProfile,
|
||||||
|
}
|
||||||
|
for _, name := range profiles {
|
||||||
|
if _, err := os.Stat(name); err != nil {
|
||||||
|
t.Errorf("Failed to create profile %s: %v", filepath.Base(name), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
util.RunAtExitFuncs()
|
||||||
|
|
||||||
|
for _, name := range profiles {
|
||||||
|
if _, err := os.Stat(name); err != nil {
|
||||||
|
t.Errorf("Failed to write profile %s: %v", filepath.Base(name), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-9
@@ -170,8 +170,8 @@ func TestParseKeys(t *testing.T) {
|
|||||||
check(tui.CtrlM, "Return")
|
check(tui.CtrlM, "Return")
|
||||||
checkEvent(tui.Key(' '), "space")
|
checkEvent(tui.Key(' '), "space")
|
||||||
check(tui.Tab, "tab")
|
check(tui.Tab, "tab")
|
||||||
check(tui.BTab, "btab")
|
check(tui.ShiftTab, "btab")
|
||||||
check(tui.ESC, "esc")
|
check(tui.Esc, "esc")
|
||||||
check(tui.Up, "up")
|
check(tui.Up, "up")
|
||||||
check(tui.Down, "down")
|
check(tui.Down, "down")
|
||||||
check(tui.Left, "left")
|
check(tui.Left, "left")
|
||||||
@@ -182,16 +182,16 @@ func TestParseKeys(t *testing.T) {
|
|||||||
t.Error(11)
|
t.Error(11)
|
||||||
}
|
}
|
||||||
check(tui.Tab, "Ctrl-I")
|
check(tui.Tab, "Ctrl-I")
|
||||||
check(tui.PgUp, "page-up")
|
check(tui.PageUp, "page-up")
|
||||||
check(tui.PgDn, "Page-Down")
|
check(tui.PageDown, "Page-Down")
|
||||||
check(tui.Home, "Home")
|
check(tui.Home, "Home")
|
||||||
check(tui.End, "End")
|
check(tui.End, "End")
|
||||||
check(tui.AltBS, "Alt-BSpace")
|
check(tui.AltBackspace, "Alt-BSpace")
|
||||||
check(tui.SLeft, "shift-left")
|
check(tui.ShiftLeft, "shift-left")
|
||||||
check(tui.SRight, "shift-right")
|
check(tui.ShiftRight, "shift-right")
|
||||||
check(tui.BTab, "shift-tab")
|
check(tui.ShiftTab, "shift-tab")
|
||||||
check(tui.CtrlM, "Enter")
|
check(tui.CtrlM, "Enter")
|
||||||
check(tui.BSpace, "bspace")
|
check(tui.Backspace, "bspace")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseKeysWithComma(t *testing.T) {
|
func TestParseKeysWithComma(t *testing.T) {
|
||||||
|
|||||||
+76
-18
@@ -1,7 +1,7 @@
|
|||||||
package fzf
|
package fzf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@@ -111,32 +111,90 @@ func (r *Reader) ReadSource(root string, opts walkerOpts, ignores []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) feed(src io.Reader) {
|
func (r *Reader) feed(src io.Reader) {
|
||||||
|
/*
|
||||||
|
readerSlabSize, ae := strconv.Atoi(os.Getenv("SLAB_KB"))
|
||||||
|
if ae != nil {
|
||||||
|
readerSlabSize = 128 * 1024
|
||||||
|
} else {
|
||||||
|
readerSlabSize *= 1024
|
||||||
|
}
|
||||||
|
readerBufferSize, be := strconv.Atoi(os.Getenv("BUF_KB"))
|
||||||
|
if be != nil {
|
||||||
|
readerBufferSize = 64 * 1024
|
||||||
|
} else {
|
||||||
|
readerBufferSize *= 1024
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
delim := byte('\n')
|
delim := byte('\n')
|
||||||
|
trimCR := util.IsWindows()
|
||||||
if r.delimNil {
|
if r.delimNil {
|
||||||
delim = '\000'
|
delim = '\000'
|
||||||
|
trimCR = false
|
||||||
}
|
}
|
||||||
reader := bufio.NewReaderSize(src, readerBufferSize)
|
|
||||||
|
slab := make([]byte, readerSlabSize)
|
||||||
|
leftover := []byte{}
|
||||||
|
var err error
|
||||||
for {
|
for {
|
||||||
// ReadBytes returns err != nil if and only if the returned data does not
|
n := 0
|
||||||
// end in delim.
|
scope := slab[:util.Min(len(slab), readerBufferSize)]
|
||||||
bytea, err := reader.ReadBytes(delim)
|
for i := 0; i < 100; i++ {
|
||||||
byteaLen := len(bytea)
|
n, err = src.Read(scope)
|
||||||
if byteaLen > 0 {
|
if n > 0 || err != nil {
|
||||||
if err == nil {
|
break
|
||||||
// get rid of carriage return if under Windows:
|
|
||||||
if util.IsWindows() && byteaLen >= 2 && bytea[byteaLen-2] == byte('\r') {
|
|
||||||
bytea = bytea[:byteaLen-2]
|
|
||||||
} else {
|
|
||||||
bytea = bytea[:byteaLen-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if r.pusher(bytea) {
|
|
||||||
atomic.StoreInt32(&r.event, int32(EvtReadNew))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
|
// We're not making any progress after 100 tries. Stop.
|
||||||
|
if n == 0 && err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf := slab[:n]
|
||||||
|
slab = slab[n:]
|
||||||
|
|
||||||
|
for len(buf) > 0 {
|
||||||
|
if i := bytes.IndexByte(buf, delim); i >= 0 {
|
||||||
|
// Found the delimiter
|
||||||
|
slice := buf[:i+1]
|
||||||
|
buf = buf[i+1:]
|
||||||
|
if trimCR && len(slice) >= 2 && slice[len(slice)-2] == byte('\r') {
|
||||||
|
slice = slice[:len(slice)-2]
|
||||||
|
} else {
|
||||||
|
slice = slice[:len(slice)-1]
|
||||||
|
}
|
||||||
|
if len(leftover) > 0 {
|
||||||
|
slice = append(leftover, slice...)
|
||||||
|
leftover = []byte{}
|
||||||
|
}
|
||||||
|
if (err == nil || len(slice) > 0) && r.pusher(slice) {
|
||||||
|
atomic.StoreInt32(&r.event, int32(EvtReadNew))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Could not find the delimiter in the buffer
|
||||||
|
// NOTE: We can further optimize this by keeping track of the cursor
|
||||||
|
// position in the slab so that a straddling item that doesn't go
|
||||||
|
// beyond the boundary of a slab doesn't need to be copied to
|
||||||
|
// another buffer. However, the performance gain is negligible in
|
||||||
|
// practice (< 0.1%) and is not
|
||||||
|
// worth the added complexity.
|
||||||
|
leftover = append(leftover, buf...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
leftover = append(leftover, buf...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(slab) == 0 {
|
||||||
|
slab = make([]byte, readerSlabSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(leftover) > 0 && r.pusher(leftover) {
|
||||||
|
atomic.StoreInt32(&r.event, int32(EvtReadNew))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -32,6 +32,7 @@ const (
|
|||||||
httpUnauthorized = "HTTP/1.1 401 Unauthorized" + crlf
|
httpUnauthorized = "HTTP/1.1 401 Unauthorized" + crlf
|
||||||
httpUnavailable = "HTTP/1.1 503 Service Unavailable" + crlf
|
httpUnavailable = "HTTP/1.1 503 Service Unavailable" + crlf
|
||||||
httpReadTimeout = 10 * time.Second
|
httpReadTimeout = 10 * time.Second
|
||||||
|
channelTimeout = 2 * time.Second
|
||||||
jsonContentType = "Content-Type: application/json" + crlf
|
jsonContentType = "Content-Type: application/json" + crlf
|
||||||
maxContentLength = 1024 * 1024
|
maxContentLength = 1024 * 1024
|
||||||
)
|
)
|
||||||
@@ -170,7 +171,7 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string {
|
|||||||
select {
|
select {
|
||||||
case response := <-server.responseChannel:
|
case response := <-server.responseChannel:
|
||||||
return good(response)
|
return good(response)
|
||||||
case <-time.After(2 * time.Second):
|
case <-time.After(channelTimeout):
|
||||||
go func() {
|
go func() {
|
||||||
// Drain the channel
|
// Drain the channel
|
||||||
<-server.responseChannel
|
<-server.responseChannel
|
||||||
@@ -227,7 +228,11 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string {
|
|||||||
return bad("no action specified")
|
return bad("no action specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
server.actionChannel <- actions
|
select {
|
||||||
|
case server.actionChannel <- actions:
|
||||||
|
case <-time.After(channelTimeout):
|
||||||
|
return httpUnavailable + crlf
|
||||||
|
}
|
||||||
return httpOk + crlf
|
return httpOk + crlf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+154
-91
@@ -55,6 +55,9 @@ var actionTypeRegex *regexp.Regexp
|
|||||||
|
|
||||||
const clearCode string = "\x1b[2J"
|
const clearCode string = "\x1b[2J"
|
||||||
|
|
||||||
|
// Number of maximum focus events to process synchronously
|
||||||
|
const maxFocusEvents = 10000
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{fzf:(?:query|action|prompt)}|{\+?f?nf?})`)
|
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{fzf:(?:query|action|prompt)}|{\+?f?nf?})`)
|
||||||
whiteSuffix = regexp.MustCompile(`\s*$`)
|
whiteSuffix = regexp.MustCompile(`\s*$`)
|
||||||
@@ -178,7 +181,7 @@ type Status struct {
|
|||||||
type Terminal struct {
|
type Terminal struct {
|
||||||
initDelay time.Duration
|
initDelay time.Duration
|
||||||
infoStyle infoStyle
|
infoStyle infoStyle
|
||||||
infoSep string
|
infoPrefix string
|
||||||
separator labelPrinter
|
separator labelPrinter
|
||||||
separatorLen int
|
separatorLen int
|
||||||
spinner []string
|
spinner []string
|
||||||
@@ -290,6 +293,7 @@ type Terminal struct {
|
|||||||
executing *util.AtomicBool
|
executing *util.AtomicBool
|
||||||
termSize tui.TermSize
|
termSize tui.TermSize
|
||||||
lastAction actionType
|
lastAction actionType
|
||||||
|
lastKey string
|
||||||
lastFocus int32
|
lastFocus int32
|
||||||
areaLines int
|
areaLines int
|
||||||
areaColumns int
|
areaColumns int
|
||||||
@@ -391,8 +395,10 @@ const (
|
|||||||
actToggleIn
|
actToggleIn
|
||||||
actToggleOut
|
actToggleOut
|
||||||
actToggleTrack
|
actToggleTrack
|
||||||
|
actToggleTrackCurrent
|
||||||
actToggleHeader
|
actToggleHeader
|
||||||
actTrack
|
actTrackCurrent
|
||||||
|
actUntrackCurrent
|
||||||
actDown
|
actDown
|
||||||
actUp
|
actUp
|
||||||
actPageUp
|
actPageUp
|
||||||
@@ -403,7 +409,7 @@ const (
|
|||||||
actOffsetUp
|
actOffsetUp
|
||||||
actOffsetDown
|
actOffsetDown
|
||||||
actJump
|
actJump
|
||||||
actJumpAccept
|
actJumpAccept // XXX Deprecated in favor of jump:accept binding
|
||||||
actPrintQuery
|
actPrintQuery
|
||||||
actRefreshPreview
|
actRefreshPreview
|
||||||
actReplaceQuery
|
actReplaceQuery
|
||||||
@@ -455,14 +461,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (a actionType) Name() string {
|
func (a actionType) Name() string {
|
||||||
name := ""
|
return util.ToKebabCase(a.String()[3:])
|
||||||
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 {
|
||||||
@@ -541,14 +540,14 @@ func defaultKeymap() map[tui.Event][]*action {
|
|||||||
add(tui.CtrlC, actAbort)
|
add(tui.CtrlC, actAbort)
|
||||||
add(tui.CtrlG, actAbort)
|
add(tui.CtrlG, actAbort)
|
||||||
add(tui.CtrlQ, actAbort)
|
add(tui.CtrlQ, actAbort)
|
||||||
add(tui.ESC, actAbort)
|
add(tui.Esc, actAbort)
|
||||||
add(tui.CtrlD, actDeleteCharEof)
|
add(tui.CtrlD, actDeleteCharEof)
|
||||||
add(tui.CtrlE, actEndOfLine)
|
add(tui.CtrlE, actEndOfLine)
|
||||||
add(tui.CtrlF, actForwardChar)
|
add(tui.CtrlF, actForwardChar)
|
||||||
add(tui.CtrlH, actBackwardDeleteChar)
|
add(tui.CtrlH, actBackwardDeleteChar)
|
||||||
add(tui.BSpace, actBackwardDeleteChar)
|
add(tui.Backspace, actBackwardDeleteChar)
|
||||||
add(tui.Tab, actToggleDown)
|
add(tui.Tab, actToggleDown)
|
||||||
add(tui.BTab, actToggleUp)
|
add(tui.ShiftTab, actToggleUp)
|
||||||
add(tui.CtrlJ, actDown)
|
add(tui.CtrlJ, actDown)
|
||||||
add(tui.CtrlK, actUp)
|
add(tui.CtrlK, actUp)
|
||||||
add(tui.CtrlL, actClearScreen)
|
add(tui.CtrlL, actClearScreen)
|
||||||
@@ -563,11 +562,11 @@ func defaultKeymap() map[tui.Event][]*action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addEvent(tui.AltKey('b'), actBackwardWord)
|
addEvent(tui.AltKey('b'), actBackwardWord)
|
||||||
add(tui.SLeft, actBackwardWord)
|
add(tui.ShiftLeft, actBackwardWord)
|
||||||
addEvent(tui.AltKey('f'), actForwardWord)
|
addEvent(tui.AltKey('f'), actForwardWord)
|
||||||
add(tui.SRight, actForwardWord)
|
add(tui.ShiftRight, actForwardWord)
|
||||||
addEvent(tui.AltKey('d'), actKillWord)
|
addEvent(tui.AltKey('d'), actKillWord)
|
||||||
add(tui.AltBS, actBackwardKillWord)
|
add(tui.AltBackspace, actBackwardKillWord)
|
||||||
|
|
||||||
add(tui.Up, actUp)
|
add(tui.Up, actUp)
|
||||||
add(tui.Down, actDown)
|
add(tui.Down, actDown)
|
||||||
@@ -576,12 +575,12 @@ func defaultKeymap() map[tui.Event][]*action {
|
|||||||
|
|
||||||
add(tui.Home, actBeginningOfLine)
|
add(tui.Home, actBeginningOfLine)
|
||||||
add(tui.End, actEndOfLine)
|
add(tui.End, actEndOfLine)
|
||||||
add(tui.Del, actDeleteChar)
|
add(tui.Delete, actDeleteChar)
|
||||||
add(tui.PgUp, actPageUp)
|
add(tui.PageUp, actPageUp)
|
||||||
add(tui.PgDn, actPageDown)
|
add(tui.PageDown, actPageDown)
|
||||||
|
|
||||||
add(tui.SUp, actPreviewUp)
|
add(tui.ShiftUp, actPreviewUp)
|
||||||
add(tui.SDown, actPreviewDown)
|
add(tui.ShiftDown, actPreviewDown)
|
||||||
|
|
||||||
add(tui.Mouse, actMouse)
|
add(tui.Mouse, actMouse)
|
||||||
add(tui.LeftClick, actClick)
|
add(tui.LeftClick, actClick)
|
||||||
@@ -672,7 +671,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
if previewBox != nil && opts.Preview.aboveOrBelow() {
|
if previewBox != nil && opts.Preview.aboveOrBelow() {
|
||||||
effectiveMinHeight += 1 + borderLines(opts.Preview.border)
|
effectiveMinHeight += 1 + borderLines(opts.Preview.border)
|
||||||
}
|
}
|
||||||
if opts.InfoStyle.noExtraLine() {
|
if noSeparatorLine(opts.InfoStyle, opts.Separator == nil || uniseg.StringWidth(*opts.Separator) > 0) {
|
||||||
effectiveMinHeight--
|
effectiveMinHeight--
|
||||||
}
|
}
|
||||||
effectiveMinHeight += borderLines(opts.BorderShape)
|
effectiveMinHeight += borderLines(opts.BorderShape)
|
||||||
@@ -694,7 +693,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
t := Terminal{
|
t := Terminal{
|
||||||
initDelay: delay,
|
initDelay: delay,
|
||||||
infoStyle: opts.InfoStyle,
|
infoStyle: opts.InfoStyle,
|
||||||
infoSep: opts.InfoSep,
|
infoPrefix: opts.InfoPrefix,
|
||||||
separator: nil,
|
separator: nil,
|
||||||
spinner: makeSpinner(opts.Unicode),
|
spinner: makeSpinner(opts.Unicode),
|
||||||
promptString: opts.Prompt,
|
promptString: opts.Prompt,
|
||||||
@@ -773,7 +772,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
theme: opts.Theme,
|
theme: opts.Theme,
|
||||||
startChan: make(chan fitpad, 1),
|
startChan: make(chan fitpad, 1),
|
||||||
killChan: make(chan int),
|
killChan: make(chan int),
|
||||||
serverInputChan: make(chan []*action, 10),
|
serverInputChan: make(chan []*action, 100),
|
||||||
serverOutputChan: make(chan string),
|
serverOutputChan: make(chan string),
|
||||||
eventChan: make(chan tui.Event, 6), // (load + result + zero|one) | (focus) | (resize) | (GetChar)
|
eventChan: make(chan tui.Event, 6), // (load + result + zero|one) | (focus) | (resize) | (GetChar)
|
||||||
tui: renderer,
|
tui: renderer,
|
||||||
@@ -846,7 +845,10 @@ func (t *Terminal) environ() []string {
|
|||||||
}
|
}
|
||||||
env = append(env, "FZF_QUERY="+string(t.input))
|
env = append(env, "FZF_QUERY="+string(t.input))
|
||||||
env = append(env, "FZF_ACTION="+t.lastAction.Name())
|
env = append(env, "FZF_ACTION="+t.lastAction.Name())
|
||||||
|
env = append(env, "FZF_KEY="+t.lastKey)
|
||||||
env = append(env, "FZF_PROMPT="+string(t.promptString))
|
env = append(env, "FZF_PROMPT="+string(t.promptString))
|
||||||
|
env = append(env, "FZF_PREVIEW_LABEL="+t.previewLabelOpts.label)
|
||||||
|
env = append(env, "FZF_BORDER_LABEL="+t.borderLabelOpts.label)
|
||||||
env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", t.count))
|
env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", t.count))
|
||||||
env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.merger.Length()))
|
env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.merger.Length()))
|
||||||
env = append(env, fmt.Sprintf("FZF_SELECT_COUNT=%d", len(t.selected)))
|
env = append(env, fmt.Sprintf("FZF_SELECT_COUNT=%d", len(t.selected)))
|
||||||
@@ -875,7 +877,7 @@ func (t *Terminal) visibleHeaderLines() int {
|
|||||||
// Extra number of lines needed to display fzf
|
// Extra number of lines needed to display fzf
|
||||||
func (t *Terminal) extraLines() int {
|
func (t *Terminal) extraLines() int {
|
||||||
extra := t.visibleHeaderLines() + 1
|
extra := t.visibleHeaderLines() + 1
|
||||||
if !t.noInfoLine() {
|
if !t.noSeparatorLine() {
|
||||||
extra++
|
extra++
|
||||||
}
|
}
|
||||||
return extra
|
return extra
|
||||||
@@ -981,8 +983,18 @@ func (t *Terminal) parsePrompt(prompt string) (func(), int) {
|
|||||||
return output, promptLen
|
return output, promptLen
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) noInfoLine() bool {
|
func noSeparatorLine(style infoStyle, separator bool) bool {
|
||||||
return t.infoStyle.noExtraLine()
|
switch style {
|
||||||
|
case infoInline:
|
||||||
|
return true
|
||||||
|
case infoHidden, infoInlineRight:
|
||||||
|
return !separator
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) noSeparatorLine() bool {
|
||||||
|
return noSeparatorLine(t.infoStyle, t.separatorLen > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getScrollbar(total int, height int, offset int) (int, int) {
|
func getScrollbar(total int, height int, offset int) (int, int) {
|
||||||
@@ -1057,11 +1069,11 @@ func (t *Terminal) UpdateProgress(progress float32) {
|
|||||||
// UpdateList updates Merger to display the list
|
// UpdateList updates Merger to display the list
|
||||||
func (t *Terminal) UpdateList(merger *Merger) {
|
func (t *Terminal) UpdateList(merger *Merger) {
|
||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
var prevIndex int32 = -1
|
prevIndex := minItem.Index()
|
||||||
reset := t.revision != merger.Revision()
|
reset := t.revision != merger.Revision()
|
||||||
if !reset && t.track != trackDisabled {
|
if !reset && t.track != trackDisabled {
|
||||||
if t.merger.Length() > 0 {
|
if t.merger.Length() > 0 {
|
||||||
prevIndex = t.merger.Get(t.cy).item.Index()
|
prevIndex = t.currentIndex()
|
||||||
} else if merger.Length() > 0 {
|
} else if merger.Length() > 0 {
|
||||||
prevIndex = merger.First().item.Index()
|
prevIndex = merger.First().item.Index()
|
||||||
}
|
}
|
||||||
@@ -1234,7 +1246,7 @@ func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
|
|||||||
|
|
||||||
minAreaWidth := minWidth
|
minAreaWidth := minWidth
|
||||||
minAreaHeight := minHeight
|
minAreaHeight := minHeight
|
||||||
if t.noInfoLine() {
|
if t.noSeparatorLine() {
|
||||||
minAreaHeight -= 1
|
minAreaHeight -= 1
|
||||||
}
|
}
|
||||||
if t.needPreviewWindow() {
|
if t.needPreviewWindow() {
|
||||||
@@ -1515,7 +1527,7 @@ func (t *Terminal) move(y int, x int, clear bool) {
|
|||||||
y = h - y - 1
|
y = h - y - 1
|
||||||
case layoutReverseList:
|
case layoutReverseList:
|
||||||
n := 2 + t.visibleHeaderLines()
|
n := 2 + t.visibleHeaderLines()
|
||||||
if t.noInfoLine() {
|
if t.noSeparatorLine() {
|
||||||
n--
|
n--
|
||||||
}
|
}
|
||||||
if y < n {
|
if y < n {
|
||||||
@@ -1555,7 +1567,7 @@ func (t *Terminal) updatePromptOffset() ([]rune, []rune) {
|
|||||||
func (t *Terminal) promptLine() int {
|
func (t *Terminal) promptLine() int {
|
||||||
if t.headerFirst {
|
if t.headerFirst {
|
||||||
max := t.window.Height() - 1
|
max := t.window.Height() - 1
|
||||||
if !t.noInfoLine() {
|
if !t.noSeparatorLine() {
|
||||||
max--
|
max--
|
||||||
}
|
}
|
||||||
return util.Min(t.visibleHeaderLines(), max)
|
return util.Min(t.visibleHeaderLines(), max)
|
||||||
@@ -1600,20 +1612,8 @@ func (t *Terminal) printInfo() {
|
|||||||
t.window.Print(" ") // Clear spinner
|
t.window.Print(" ") // Clear spinner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch t.infoStyle {
|
printInfoPrefix := func() {
|
||||||
case infoDefault:
|
str := t.infoPrefix
|
||||||
t.move(line+1, 0, t.separatorLen == 0)
|
|
||||||
printSpinner()
|
|
||||||
t.move(line+1, 2, false)
|
|
||||||
pos = 2
|
|
||||||
case infoRight:
|
|
||||||
t.move(line+1, 0, false)
|
|
||||||
case infoInlineRight:
|
|
||||||
pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
|
|
||||||
t.move(line, pos, true)
|
|
||||||
case infoInline:
|
|
||||||
pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
|
|
||||||
str := t.infoSep
|
|
||||||
maxWidth := t.window.Width() - pos
|
maxWidth := t.window.Width() - pos
|
||||||
width := util.StringWidth(str)
|
width := util.StringWidth(str)
|
||||||
if width > maxWidth {
|
if width > maxWidth {
|
||||||
@@ -1628,7 +1628,34 @@ func (t *Terminal) printInfo() {
|
|||||||
t.window.CPrint(tui.ColPrompt, str)
|
t.window.CPrint(tui.ColPrompt, str)
|
||||||
}
|
}
|
||||||
pos += width
|
pos += width
|
||||||
|
}
|
||||||
|
printSeparator := func(fillLength int, pad bool) {
|
||||||
|
// --------_
|
||||||
|
if t.separatorLen > 0 {
|
||||||
|
t.separator(t.window, fillLength)
|
||||||
|
t.window.Print(" ")
|
||||||
|
} else if pad {
|
||||||
|
t.window.Print(strings.Repeat(" ", fillLength+1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch t.infoStyle {
|
||||||
|
case infoDefault:
|
||||||
|
t.move(line+1, 0, t.separatorLen == 0)
|
||||||
|
printSpinner()
|
||||||
|
t.move(line+1, 2, false)
|
||||||
|
pos = 2
|
||||||
|
case infoRight:
|
||||||
|
t.move(line+1, 0, false)
|
||||||
|
case infoInlineRight:
|
||||||
|
pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
|
||||||
|
case infoInline:
|
||||||
|
pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
|
||||||
|
printInfoPrefix()
|
||||||
case infoHidden:
|
case infoHidden:
|
||||||
|
if t.separatorLen > 0 {
|
||||||
|
t.move(line+1, 0, false)
|
||||||
|
printSeparator(t.window.Width()-1, false)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1642,8 +1669,11 @@ func (t *Terminal) printInfo() {
|
|||||||
output += " -S"
|
output += " -S"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t.track != trackDisabled {
|
switch t.track {
|
||||||
|
case trackEnabled:
|
||||||
output += " +T"
|
output += " +T"
|
||||||
|
case trackCurrent:
|
||||||
|
output += " +t"
|
||||||
}
|
}
|
||||||
if t.multi > 0 {
|
if t.multi > 0 {
|
||||||
if t.multi == maxMulti {
|
if t.multi == maxMulti {
|
||||||
@@ -1659,15 +1689,6 @@ func (t *Terminal) printInfo() {
|
|||||||
output = fmt.Sprintf("[Command failed: %s]", *t.failed)
|
output = fmt.Sprintf("[Command failed: %s]", *t.failed)
|
||||||
}
|
}
|
||||||
|
|
||||||
printSeparator := func(fillLength int, pad bool) {
|
|
||||||
// --------_
|
|
||||||
if t.separatorLen > 0 {
|
|
||||||
t.separator(t.window, fillLength)
|
|
||||||
t.window.Print(" ")
|
|
||||||
} else if pad {
|
|
||||||
t.window.Print(strings.Repeat(" ", fillLength+1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t.infoStyle == infoRight {
|
if t.infoStyle == infoRight {
|
||||||
maxWidth := t.window.Width()
|
maxWidth := t.window.Width()
|
||||||
if t.reading {
|
if t.reading {
|
||||||
@@ -1690,19 +1711,35 @@ func (t *Terminal) printInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if t.infoStyle == infoInlineRight {
|
if t.infoStyle == infoInlineRight {
|
||||||
pos = util.Max(pos, t.window.Width()-util.StringWidth(output)-3)
|
if len(t.infoPrefix) == 0 {
|
||||||
if pos >= t.window.Width() {
|
pos = util.Max(pos, t.window.Width()-util.StringWidth(output)-3)
|
||||||
return
|
if pos < t.window.Width() {
|
||||||
|
t.move(line, pos, false)
|
||||||
|
printSpinner()
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
if pos < t.window.Width()-1 {
|
||||||
|
t.window.Print(" ")
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pos = util.Max(pos, t.window.Width()-util.StringWidth(output)-util.StringWidth(t.infoPrefix)-1)
|
||||||
|
printInfoPrefix()
|
||||||
}
|
}
|
||||||
t.move(line, pos, false)
|
|
||||||
printSpinner()
|
|
||||||
t.window.Print(" ")
|
|
||||||
pos += 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
maxWidth := t.window.Width() - pos
|
maxWidth := t.window.Width() - pos
|
||||||
output = t.trimMessage(output, maxWidth)
|
output = t.trimMessage(output, maxWidth)
|
||||||
t.window.CPrint(tui.ColInfo, output)
|
t.window.CPrint(tui.ColInfo, output)
|
||||||
|
|
||||||
|
if t.infoStyle == infoInlineRight {
|
||||||
|
if t.separatorLen > 0 {
|
||||||
|
t.move(line+1, 0, false)
|
||||||
|
printSeparator(t.window.Width()-1, false)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
fillLength := maxWidth - len(output) - 2
|
fillLength := maxWidth - len(output) - 2
|
||||||
if fillLength > 0 {
|
if fillLength > 0 {
|
||||||
t.window.CPrint(tui.ColSeparator, " ")
|
t.window.CPrint(tui.ColSeparator, " ")
|
||||||
@@ -1717,7 +1754,7 @@ func (t *Terminal) printHeader() {
|
|||||||
max := t.window.Height()
|
max := t.window.Height()
|
||||||
if t.headerFirst {
|
if t.headerFirst {
|
||||||
max--
|
max--
|
||||||
if !t.noInfoLine() {
|
if !t.noSeparatorLine() {
|
||||||
max--
|
max--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1734,7 +1771,7 @@ func (t *Terminal) printHeader() {
|
|||||||
}
|
}
|
||||||
if !t.headerFirst {
|
if !t.headerFirst {
|
||||||
line++
|
line++
|
||||||
if !t.noInfoLine() {
|
if !t.noSeparatorLine() {
|
||||||
line++
|
line++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1766,7 +1803,7 @@ func (t *Terminal) printList() {
|
|||||||
i = maxy - 1 - j
|
i = maxy - 1 - j
|
||||||
}
|
}
|
||||||
line := i + 2 + t.visibleHeaderLines()
|
line := i + 2 + t.visibleHeaderLines()
|
||||||
if t.noInfoLine() {
|
if t.noSeparatorLine() {
|
||||||
line--
|
line--
|
||||||
}
|
}
|
||||||
if i < count {
|
if i < count {
|
||||||
@@ -3092,7 +3129,7 @@ func (t *Terminal) Loop() {
|
|||||||
switch req {
|
switch req {
|
||||||
case reqPrompt:
|
case reqPrompt:
|
||||||
t.printPrompt()
|
t.printPrompt()
|
||||||
if t.noInfoLine() {
|
if t.infoStyle == infoInline || t.infoStyle == infoInlineRight {
|
||||||
t.printInfo()
|
t.printInfo()
|
||||||
}
|
}
|
||||||
case reqInfo:
|
case reqInfo:
|
||||||
@@ -3248,6 +3285,7 @@ func (t *Terminal) Loop() {
|
|||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
previousInput := t.input
|
previousInput := t.input
|
||||||
previousCx := t.cx
|
previousCx := t.cx
|
||||||
|
t.lastKey = event.KeyName()
|
||||||
events := []util.EventType{}
|
events := []util.EventType{}
|
||||||
req := func(evts ...util.EventType) {
|
req := func(evts ...util.EventType) {
|
||||||
for _, event := range evts {
|
for _, event := range evts {
|
||||||
@@ -3305,18 +3343,22 @@ func (t *Terminal) Loop() {
|
|||||||
var doAction func(*action) bool
|
var doAction func(*action) bool
|
||||||
var doActions func(actions []*action) bool
|
var doActions func(actions []*action) bool
|
||||||
doActions = func(actions []*action) bool {
|
doActions = func(actions []*action) bool {
|
||||||
currentIndex := t.currentIndex()
|
for iter := 0; iter <= maxFocusEvents; iter++ {
|
||||||
for _, action := range actions {
|
currentIndex := t.currentIndex()
|
||||||
if !doAction(action) {
|
for _, action := range actions {
|
||||||
return false
|
if !doAction(action) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if onFocus, prs := t.keymap[tui.Focus.AsEvent()]; prs {
|
if onFocus, prs := t.keymap[tui.Focus.AsEvent()]; prs && iter < maxFocusEvents {
|
||||||
if newIndex := t.currentIndex(); newIndex != currentIndex {
|
if newIndex := t.currentIndex(); newIndex != currentIndex {
|
||||||
t.lastFocus = newIndex
|
t.lastFocus = newIndex
|
||||||
return doActions(onFocus)
|
actions = onFocus
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -3464,11 +3506,13 @@ func (t *Terminal) Loop() {
|
|||||||
req(reqHeader)
|
req(reqHeader)
|
||||||
}
|
}
|
||||||
case actChangeBorderLabel:
|
case actChangeBorderLabel:
|
||||||
|
t.borderLabelOpts.label = a.a
|
||||||
if t.border != nil {
|
if t.border != nil {
|
||||||
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(a.a, &tui.ColBorderLabel, false)
|
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(a.a, &tui.ColBorderLabel, false)
|
||||||
req(reqRedrawBorderLabel)
|
req(reqRedrawBorderLabel)
|
||||||
}
|
}
|
||||||
case actChangePreviewLabel:
|
case actChangePreviewLabel:
|
||||||
|
t.previewLabelOpts.label = a.a
|
||||||
if t.pborder != nil {
|
if t.pborder != nil {
|
||||||
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(a.a, &tui.ColPreviewLabel, false)
|
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(a.a, &tui.ColPreviewLabel, false)
|
||||||
req(reqRedrawPreviewLabel)
|
req(reqRedrawPreviewLabel)
|
||||||
@@ -3478,14 +3522,16 @@ func (t *Terminal) Loop() {
|
|||||||
actions := parseSingleActionList(strings.Trim(body, "\r\n"), func(message string) {})
|
actions := parseSingleActionList(strings.Trim(body, "\r\n"), func(message string) {})
|
||||||
return doActions(actions)
|
return doActions(actions)
|
||||||
case actTransformBorderLabel:
|
case actTransformBorderLabel:
|
||||||
|
label := t.executeCommand(a.a, false, true, true, true)
|
||||||
|
t.borderLabelOpts.label = label
|
||||||
if t.border != nil {
|
if t.border != nil {
|
||||||
label := t.executeCommand(a.a, false, true, true, true)
|
|
||||||
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(label, &tui.ColBorderLabel, false)
|
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(label, &tui.ColBorderLabel, false)
|
||||||
req(reqRedrawBorderLabel)
|
req(reqRedrawBorderLabel)
|
||||||
}
|
}
|
||||||
case actTransformPreviewLabel:
|
case actTransformPreviewLabel:
|
||||||
|
label := t.executeCommand(a.a, false, true, true, true)
|
||||||
|
t.previewLabelOpts.label = label
|
||||||
if t.pborder != nil {
|
if t.pborder != nil {
|
||||||
label := t.executeCommand(a.a, false, true, true, true)
|
|
||||||
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(label, &tui.ColPreviewLabel, false)
|
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(label, &tui.ColPreviewLabel, false)
|
||||||
req(reqRedrawPreviewLabel)
|
req(reqRedrawPreviewLabel)
|
||||||
}
|
}
|
||||||
@@ -3771,6 +3817,14 @@ func (t *Terminal) Loop() {
|
|||||||
t.track = trackEnabled
|
t.track = trackEnabled
|
||||||
}
|
}
|
||||||
req(reqInfo)
|
req(reqInfo)
|
||||||
|
case actToggleTrackCurrent:
|
||||||
|
switch t.track {
|
||||||
|
case trackCurrent:
|
||||||
|
t.track = trackDisabled
|
||||||
|
case trackDisabled:
|
||||||
|
t.track = trackCurrent
|
||||||
|
}
|
||||||
|
req(reqInfo)
|
||||||
case actShowHeader:
|
case actShowHeader:
|
||||||
t.headerVisible = true
|
t.headerVisible = true
|
||||||
req(reqList, reqInfo, reqPrompt, reqHeader)
|
req(reqList, reqInfo, reqPrompt, reqHeader)
|
||||||
@@ -3780,11 +3834,16 @@ func (t *Terminal) Loop() {
|
|||||||
case actToggleHeader:
|
case actToggleHeader:
|
||||||
t.headerVisible = !t.headerVisible
|
t.headerVisible = !t.headerVisible
|
||||||
req(reqList, reqInfo, reqPrompt, reqHeader)
|
req(reqList, reqInfo, reqPrompt, reqHeader)
|
||||||
case actTrack:
|
case actTrackCurrent:
|
||||||
if t.track == trackDisabled {
|
if t.track == trackDisabled {
|
||||||
t.track = trackCurrent
|
t.track = trackCurrent
|
||||||
}
|
}
|
||||||
req(reqInfo)
|
req(reqInfo)
|
||||||
|
case actUntrackCurrent:
|
||||||
|
if t.track == trackCurrent {
|
||||||
|
t.track = trackDisabled
|
||||||
|
}
|
||||||
|
req(reqInfo)
|
||||||
case actEnableSearch:
|
case actEnableSearch:
|
||||||
t.paused = false
|
t.paused = false
|
||||||
changed = true
|
changed = true
|
||||||
@@ -3873,7 +3932,7 @@ func (t *Terminal) Loop() {
|
|||||||
mx -= t.window.Left()
|
mx -= t.window.Left()
|
||||||
my -= t.window.Top()
|
my -= t.window.Top()
|
||||||
min := 2 + t.visibleHeaderLines()
|
min := 2 + t.visibleHeaderLines()
|
||||||
if t.noInfoLine() {
|
if t.noSeparatorLine() {
|
||||||
min--
|
min--
|
||||||
}
|
}
|
||||||
h := t.window.Height()
|
h := t.window.Height()
|
||||||
@@ -4045,6 +4104,9 @@ func (t *Terminal) Loop() {
|
|||||||
// Break out of jump mode if any action is submitted to the server
|
// Break out of jump mode if any action is submitted to the server
|
||||||
if t.jumping != jumpDisabled {
|
if t.jumping != jumpDisabled {
|
||||||
t.jumping = jumpDisabled
|
t.jumping = jumpDisabled
|
||||||
|
if acts, prs := t.keymap[tui.JumpCancel.AsEvent()]; prs && !doActions(acts) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
req(reqList)
|
req(reqList)
|
||||||
}
|
}
|
||||||
if len(actions) == 0 {
|
if len(actions) == 0 {
|
||||||
@@ -4058,19 +4120,17 @@ func (t *Terminal) Loop() {
|
|||||||
t.truncateQuery()
|
t.truncateQuery()
|
||||||
queryChanged = string(previousInput) != string(t.input)
|
queryChanged = string(previousInput) != string(t.input)
|
||||||
changed = changed || queryChanged
|
changed = changed || queryChanged
|
||||||
if onChanges, prs := t.keymap[tui.Change.AsEvent()]; queryChanged && prs {
|
if onChanges, prs := t.keymap[tui.Change.AsEvent()]; queryChanged && prs && !doActions(onChanges) {
|
||||||
if !doActions(onChanges) {
|
continue
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if onEOFs, prs := t.keymap[tui.BackwardEOF.AsEvent()]; beof && prs {
|
if onEOFs, prs := t.keymap[tui.BackwardEOF.AsEvent()]; beof && prs && !doActions(onEOFs) {
|
||||||
if !doActions(onEOFs) {
|
continue
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
jumpEvent := tui.JumpCancel
|
||||||
if event.Type == tui.Rune {
|
if event.Type == tui.Rune {
|
||||||
if idx := strings.IndexRune(t.jumpLabels, event.Char); idx >= 0 && idx < t.maxItems() && idx < t.merger.Length() {
|
if idx := strings.IndexRune(t.jumpLabels, event.Char); idx >= 0 && idx < t.maxItems() && idx < t.merger.Length() {
|
||||||
|
jumpEvent = tui.Jump
|
||||||
t.cy = idx + t.offset
|
t.cy = idx + t.offset
|
||||||
if t.jumping == jumpAcceptEnabled {
|
if t.jumping == jumpAcceptEnabled {
|
||||||
req(reqClose)
|
req(reqClose)
|
||||||
@@ -4078,6 +4138,9 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.jumping = jumpDisabled
|
t.jumping = jumpDisabled
|
||||||
|
if acts, prs := t.keymap[jumpEvent.AsEvent()]; prs && !doActions(acts) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
req(reqList)
|
req(reqList)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4114,7 +4177,7 @@ func (t *Terminal) constrain() {
|
|||||||
// count of lines can be displayed
|
// count of lines can be displayed
|
||||||
height := t.maxItems()
|
height := t.maxItems()
|
||||||
|
|
||||||
t.cy = util.Constrain(t.cy, 0, count-1)
|
t.cy = util.Constrain(t.cy, 0, util.Max(0, count-1))
|
||||||
|
|
||||||
minOffset := util.Max(t.cy-height+1, 0)
|
minOffset := util.Max(t.cy-height+1, 0)
|
||||||
maxOffset := util.Max(util.Min(count-height, t.cy), 0)
|
maxOffset := util.Max(util.Min(count-height, t.cy), 0)
|
||||||
@@ -4165,7 +4228,7 @@ func (t *Terminal) vset(o int) bool {
|
|||||||
|
|
||||||
func (t *Terminal) maxItems() int {
|
func (t *Terminal) maxItems() int {
|
||||||
max := t.window.Height() - 2 - t.visibleHeaderLines()
|
max := t.window.Height() - 2 - t.visibleHeaderLines()
|
||||||
if t.noInfoLine() {
|
if t.noSeparatorLine() {
|
||||||
max++
|
max++
|
||||||
}
|
}
|
||||||
return util.Max(max, 0)
|
return util.Max(max, 0)
|
||||||
|
|||||||
+2
-2
@@ -91,7 +91,7 @@ func withPrefixLengths(tokens []string, begin int) []Token {
|
|||||||
|
|
||||||
prefixLength := begin
|
prefixLength := begin
|
||||||
for idx := range tokens {
|
for idx := range tokens {
|
||||||
chars := util.ToChars([]byte(tokens[idx]))
|
chars := util.ToChars(sbytes(tokens[idx]))
|
||||||
ret[idx] = Token{&chars, int32(prefixLength)}
|
ret[idx] = Token{&chars, int32(prefixLength)}
|
||||||
prefixLength += chars.Length()
|
prefixLength += chars.Length()
|
||||||
}
|
}
|
||||||
@@ -187,7 +187,7 @@ func Transform(tokens []Token, withNth []Range) []Token {
|
|||||||
if r.begin == r.end {
|
if r.begin == r.end {
|
||||||
idx := r.begin
|
idx := r.begin
|
||||||
if idx == rangeEllipsis {
|
if idx == rangeEllipsis {
|
||||||
chars := util.ToChars([]byte(joinTokens(tokens)))
|
chars := util.ToChars(sbytes(joinTokens(tokens)))
|
||||||
parts = append(parts, &chars)
|
parts = append(parts, &chars)
|
||||||
} else {
|
} else {
|
||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
// Code generated by "stringer -type=EventType"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[Rune-0]
|
||||||
|
_ = x[CtrlA-1]
|
||||||
|
_ = x[CtrlB-2]
|
||||||
|
_ = x[CtrlC-3]
|
||||||
|
_ = x[CtrlD-4]
|
||||||
|
_ = x[CtrlE-5]
|
||||||
|
_ = x[CtrlF-6]
|
||||||
|
_ = x[CtrlG-7]
|
||||||
|
_ = x[CtrlH-8]
|
||||||
|
_ = x[Tab-9]
|
||||||
|
_ = x[CtrlJ-10]
|
||||||
|
_ = x[CtrlK-11]
|
||||||
|
_ = x[CtrlL-12]
|
||||||
|
_ = x[CtrlM-13]
|
||||||
|
_ = x[CtrlN-14]
|
||||||
|
_ = x[CtrlO-15]
|
||||||
|
_ = x[CtrlP-16]
|
||||||
|
_ = x[CtrlQ-17]
|
||||||
|
_ = x[CtrlR-18]
|
||||||
|
_ = x[CtrlS-19]
|
||||||
|
_ = x[CtrlT-20]
|
||||||
|
_ = x[CtrlU-21]
|
||||||
|
_ = x[CtrlV-22]
|
||||||
|
_ = x[CtrlW-23]
|
||||||
|
_ = x[CtrlX-24]
|
||||||
|
_ = x[CtrlY-25]
|
||||||
|
_ = x[CtrlZ-26]
|
||||||
|
_ = x[Esc-27]
|
||||||
|
_ = x[CtrlSpace-28]
|
||||||
|
_ = x[CtrlDelete-29]
|
||||||
|
_ = x[CtrlBackSlash-30]
|
||||||
|
_ = x[CtrlRightBracket-31]
|
||||||
|
_ = x[CtrlCaret-32]
|
||||||
|
_ = x[CtrlSlash-33]
|
||||||
|
_ = x[ShiftTab-34]
|
||||||
|
_ = x[Backspace-35]
|
||||||
|
_ = x[Delete-36]
|
||||||
|
_ = x[PageUp-37]
|
||||||
|
_ = x[PageDown-38]
|
||||||
|
_ = x[Up-39]
|
||||||
|
_ = x[Down-40]
|
||||||
|
_ = x[Left-41]
|
||||||
|
_ = x[Right-42]
|
||||||
|
_ = x[Home-43]
|
||||||
|
_ = x[End-44]
|
||||||
|
_ = x[Insert-45]
|
||||||
|
_ = x[ShiftUp-46]
|
||||||
|
_ = x[ShiftDown-47]
|
||||||
|
_ = x[ShiftLeft-48]
|
||||||
|
_ = x[ShiftRight-49]
|
||||||
|
_ = x[ShiftDelete-50]
|
||||||
|
_ = x[F1-51]
|
||||||
|
_ = x[F2-52]
|
||||||
|
_ = x[F3-53]
|
||||||
|
_ = x[F4-54]
|
||||||
|
_ = x[F5-55]
|
||||||
|
_ = x[F6-56]
|
||||||
|
_ = x[F7-57]
|
||||||
|
_ = x[F8-58]
|
||||||
|
_ = x[F9-59]
|
||||||
|
_ = x[F10-60]
|
||||||
|
_ = x[F11-61]
|
||||||
|
_ = x[F12-62]
|
||||||
|
_ = x[AltBackspace-63]
|
||||||
|
_ = x[AltUp-64]
|
||||||
|
_ = x[AltDown-65]
|
||||||
|
_ = x[AltLeft-66]
|
||||||
|
_ = x[AltRight-67]
|
||||||
|
_ = x[AltShiftUp-68]
|
||||||
|
_ = x[AltShiftDown-69]
|
||||||
|
_ = x[AltShiftLeft-70]
|
||||||
|
_ = x[AltShiftRight-71]
|
||||||
|
_ = x[Alt-72]
|
||||||
|
_ = x[CtrlAlt-73]
|
||||||
|
_ = x[Invalid-74]
|
||||||
|
_ = x[Mouse-75]
|
||||||
|
_ = x[DoubleClick-76]
|
||||||
|
_ = x[LeftClick-77]
|
||||||
|
_ = x[RightClick-78]
|
||||||
|
_ = x[SLeftClick-79]
|
||||||
|
_ = x[SRightClick-80]
|
||||||
|
_ = x[ScrollUp-81]
|
||||||
|
_ = x[ScrollDown-82]
|
||||||
|
_ = x[SScrollUp-83]
|
||||||
|
_ = x[SScrollDown-84]
|
||||||
|
_ = x[PreviewScrollUp-85]
|
||||||
|
_ = x[PreviewScrollDown-86]
|
||||||
|
_ = x[Resize-87]
|
||||||
|
_ = x[Change-88]
|
||||||
|
_ = x[BackwardEOF-89]
|
||||||
|
_ = x[Start-90]
|
||||||
|
_ = x[Load-91]
|
||||||
|
_ = x[Focus-92]
|
||||||
|
_ = x[One-93]
|
||||||
|
_ = x[Zero-94]
|
||||||
|
_ = x[Result-95]
|
||||||
|
_ = x[Jump-96]
|
||||||
|
_ = x[JumpCancel-97]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _EventType_name = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlHTabCtrlJCtrlKCtrlLCtrlMCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlDeleteCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltCtrlAltInvalidMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancel"
|
||||||
|
|
||||||
|
var _EventType_index = [...]uint16{0, 4, 9, 14, 19, 24, 29, 34, 39, 44, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117, 122, 127, 132, 135, 144, 154, 167, 183, 192, 201, 209, 218, 224, 230, 238, 240, 244, 248, 253, 257, 260, 266, 273, 282, 291, 301, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 333, 336, 339, 351, 356, 363, 370, 378, 388, 400, 412, 425, 428, 435, 442, 447, 458, 467, 477, 487, 498, 506, 516, 525, 536, 551, 568, 574, 580, 591, 596, 600, 605, 608, 612, 618, 622, 632}
|
||||||
|
|
||||||
|
func (i EventType) String() string {
|
||||||
|
if i < 0 || i >= EventType(len(_EventType_index)-1) {
|
||||||
|
return "EventType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _EventType_name[_EventType_index[i]:_EventType_index[i+1]]
|
||||||
|
}
|
||||||
+40
-27
@@ -71,7 +71,7 @@ func (r *LightRenderer) csi(code string) string {
|
|||||||
|
|
||||||
func (r *LightRenderer) flush() {
|
func (r *LightRenderer) flush() {
|
||||||
if r.queued.Len() > 0 {
|
if r.queued.Len() > 0 {
|
||||||
fmt.Fprint(os.Stderr, "\x1b[?25l"+r.queued.String()+"\x1b[?25h")
|
fmt.Fprint(os.Stderr, "\x1b[?7l\x1b[?25l"+r.queued.String()+"\x1b[?25h\x1b[?7h")
|
||||||
r.queued.Reset()
|
r.queued.Reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,7 +245,7 @@ func (r *LightRenderer) getBytesInternal(buffer []byte, nonblock bool) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
retries := 0
|
retries := 0
|
||||||
if c == ESC.Int() || nonblock {
|
if c == Esc.Int() || nonblock {
|
||||||
retries = r.escDelay / escPollInterval
|
retries = r.escDelay / escPollInterval
|
||||||
}
|
}
|
||||||
buffer = append(buffer, byte(c))
|
buffer = append(buffer, byte(c))
|
||||||
@@ -260,7 +260,7 @@ func (r *LightRenderer) getBytesInternal(buffer []byte, nonblock bool) []byte {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
} else if c == ESC.Int() && pc != c {
|
} else if c == Esc.Int() && pc != c {
|
||||||
retries = r.escDelay / escPollInterval
|
retries = r.escDelay / escPollInterval
|
||||||
} else {
|
} else {
|
||||||
retries = 0
|
retries = 0
|
||||||
@@ -300,7 +300,7 @@ func (r *LightRenderer) GetChar() Event {
|
|||||||
case CtrlQ.Byte():
|
case CtrlQ.Byte():
|
||||||
return Event{CtrlQ, 0, nil}
|
return Event{CtrlQ, 0, nil}
|
||||||
case 127:
|
case 127:
|
||||||
return Event{BSpace, 0, nil}
|
return Event{Backspace, 0, nil}
|
||||||
case 0:
|
case 0:
|
||||||
return Event{CtrlSpace, 0, nil}
|
return Event{CtrlSpace, 0, nil}
|
||||||
case 28:
|
case 28:
|
||||||
@@ -311,7 +311,7 @@ func (r *LightRenderer) GetChar() Event {
|
|||||||
return Event{CtrlCaret, 0, nil}
|
return Event{CtrlCaret, 0, nil}
|
||||||
case 31:
|
case 31:
|
||||||
return Event{CtrlSlash, 0, nil}
|
return Event{CtrlSlash, 0, nil}
|
||||||
case ESC.Byte():
|
case Esc.Byte():
|
||||||
ev := r.escSequence(&sz)
|
ev := r.escSequence(&sz)
|
||||||
// Second chance
|
// Second chance
|
||||||
if ev.Type == Invalid {
|
if ev.Type == Invalid {
|
||||||
@@ -327,7 +327,7 @@ func (r *LightRenderer) GetChar() Event {
|
|||||||
}
|
}
|
||||||
char, rsz := utf8.DecodeRune(r.buffer)
|
char, rsz := utf8.DecodeRune(r.buffer)
|
||||||
if char == utf8.RuneError {
|
if char == utf8.RuneError {
|
||||||
return Event{ESC, 0, nil}
|
return Event{Esc, 0, nil}
|
||||||
}
|
}
|
||||||
sz = rsz
|
sz = rsz
|
||||||
return Event{Rune, char, nil}
|
return Event{Rune, char, nil}
|
||||||
@@ -335,7 +335,7 @@ func (r *LightRenderer) GetChar() Event {
|
|||||||
|
|
||||||
func (r *LightRenderer) escSequence(sz *int) Event {
|
func (r *LightRenderer) escSequence(sz *int) Event {
|
||||||
if len(r.buffer) < 2 {
|
if len(r.buffer) < 2 {
|
||||||
return Event{ESC, 0, nil}
|
return Event{Esc, 0, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
loc := offsetRegexpBegin.FindIndex(r.buffer)
|
loc := offsetRegexpBegin.FindIndex(r.buffer)
|
||||||
@@ -349,15 +349,15 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
|||||||
return CtrlAltKey(rune(r.buffer[1] + 'a' - 1))
|
return CtrlAltKey(rune(r.buffer[1] + 'a' - 1))
|
||||||
}
|
}
|
||||||
alt := false
|
alt := false
|
||||||
if len(r.buffer) > 2 && r.buffer[1] == ESC.Byte() {
|
if len(r.buffer) > 2 && r.buffer[1] == Esc.Byte() {
|
||||||
r.buffer = r.buffer[1:]
|
r.buffer = r.buffer[1:]
|
||||||
alt = true
|
alt = true
|
||||||
}
|
}
|
||||||
switch r.buffer[1] {
|
switch r.buffer[1] {
|
||||||
case ESC.Byte():
|
case Esc.Byte():
|
||||||
return Event{ESC, 0, nil}
|
return Event{Esc, 0, nil}
|
||||||
case 127:
|
case 127:
|
||||||
return Event{AltBS, 0, nil}
|
return Event{AltBackspace, 0, nil}
|
||||||
case '[', 'O':
|
case '[', 'O':
|
||||||
if len(r.buffer) < 3 {
|
if len(r.buffer) < 3 {
|
||||||
return Event{Invalid, 0, nil}
|
return Event{Invalid, 0, nil}
|
||||||
@@ -386,7 +386,7 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
|||||||
}
|
}
|
||||||
return Event{Up, 0, nil}
|
return Event{Up, 0, nil}
|
||||||
case 'Z':
|
case 'Z':
|
||||||
return Event{BTab, 0, nil}
|
return Event{ShiftTab, 0, nil}
|
||||||
case 'H':
|
case 'H':
|
||||||
return Event{Home, 0, nil}
|
return Event{Home, 0, nil}
|
||||||
case 'F':
|
case 'F':
|
||||||
@@ -434,7 +434,7 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
|||||||
return Event{Invalid, 0, nil} // INS
|
return Event{Invalid, 0, nil} // INS
|
||||||
case '3':
|
case '3':
|
||||||
if r.buffer[3] == '~' {
|
if r.buffer[3] == '~' {
|
||||||
return Event{Del, 0, nil}
|
return Event{Delete, 0, nil}
|
||||||
}
|
}
|
||||||
if len(r.buffer) == 6 && r.buffer[5] == '~' {
|
if len(r.buffer) == 6 && r.buffer[5] == '~' {
|
||||||
*sz = 6
|
*sz = 6
|
||||||
@@ -442,16 +442,16 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
|||||||
case '5':
|
case '5':
|
||||||
return Event{CtrlDelete, 0, nil}
|
return Event{CtrlDelete, 0, nil}
|
||||||
case '2':
|
case '2':
|
||||||
return Event{SDelete, 0, nil}
|
return Event{ShiftDelete, 0, nil}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Event{Invalid, 0, nil}
|
return Event{Invalid, 0, nil}
|
||||||
case '4':
|
case '4':
|
||||||
return Event{End, 0, nil}
|
return Event{End, 0, nil}
|
||||||
case '5':
|
case '5':
|
||||||
return Event{PgUp, 0, nil}
|
return Event{PageUp, 0, nil}
|
||||||
case '6':
|
case '6':
|
||||||
return Event{PgDn, 0, nil}
|
return Event{PageDown, 0, nil}
|
||||||
case '7':
|
case '7':
|
||||||
return Event{Home, 0, nil}
|
return Event{Home, 0, nil}
|
||||||
case '8':
|
case '8':
|
||||||
@@ -489,16 +489,29 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
|||||||
}
|
}
|
||||||
*sz = 6
|
*sz = 6
|
||||||
switch r.buffer[4] {
|
switch r.buffer[4] {
|
||||||
case '1', '2', '3', '5':
|
case '1', '2', '3', '4', '5':
|
||||||
|
// Kitty iTerm2 WezTerm
|
||||||
|
// SHIFT-ARROW "\e[1;2D"
|
||||||
|
// ALT-SHIFT-ARROW "\e[1;4D" "\e[1;10D" "\e[1;4D"
|
||||||
|
// CTRL-SHIFT-ARROW "\e[1;6D" N/A
|
||||||
|
// CMD-SHIFT-ARROW "\e[1;10D" N/A N/A ("\e[1;2D")
|
||||||
alt := r.buffer[4] == '3'
|
alt := r.buffer[4] == '3'
|
||||||
altShift := r.buffer[4] == '1' && r.buffer[5] == '0'
|
|
||||||
char := r.buffer[5]
|
char := r.buffer[5]
|
||||||
if altShift {
|
altShift := false
|
||||||
|
if r.buffer[4] == '1' && r.buffer[5] == '0' {
|
||||||
|
altShift = true
|
||||||
if len(r.buffer) < 7 {
|
if len(r.buffer) < 7 {
|
||||||
return Event{Invalid, 0, nil}
|
return Event{Invalid, 0, nil}
|
||||||
}
|
}
|
||||||
*sz = 7
|
*sz = 7
|
||||||
char = r.buffer[6]
|
char = r.buffer[6]
|
||||||
|
} else if r.buffer[4] == '4' {
|
||||||
|
altShift = true
|
||||||
|
if len(r.buffer) < 6 {
|
||||||
|
return Event{Invalid, 0, nil}
|
||||||
|
}
|
||||||
|
*sz = 6
|
||||||
|
char = r.buffer[5]
|
||||||
}
|
}
|
||||||
switch char {
|
switch char {
|
||||||
case 'A':
|
case 'A':
|
||||||
@@ -506,33 +519,33 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
|||||||
return Event{AltUp, 0, nil}
|
return Event{AltUp, 0, nil}
|
||||||
}
|
}
|
||||||
if altShift {
|
if altShift {
|
||||||
return Event{AltSUp, 0, nil}
|
return Event{AltShiftUp, 0, nil}
|
||||||
}
|
}
|
||||||
return Event{SUp, 0, nil}
|
return Event{ShiftUp, 0, nil}
|
||||||
case 'B':
|
case 'B':
|
||||||
if alt {
|
if alt {
|
||||||
return Event{AltDown, 0, nil}
|
return Event{AltDown, 0, nil}
|
||||||
}
|
}
|
||||||
if altShift {
|
if altShift {
|
||||||
return Event{AltSDown, 0, nil}
|
return Event{AltShiftDown, 0, nil}
|
||||||
}
|
}
|
||||||
return Event{SDown, 0, nil}
|
return Event{ShiftDown, 0, nil}
|
||||||
case 'C':
|
case 'C':
|
||||||
if alt {
|
if alt {
|
||||||
return Event{AltRight, 0, nil}
|
return Event{AltRight, 0, nil}
|
||||||
}
|
}
|
||||||
if altShift {
|
if altShift {
|
||||||
return Event{AltSRight, 0, nil}
|
return Event{AltShiftRight, 0, nil}
|
||||||
}
|
}
|
||||||
return Event{SRight, 0, nil}
|
return Event{ShiftRight, 0, nil}
|
||||||
case 'D':
|
case 'D':
|
||||||
if alt {
|
if alt {
|
||||||
return Event{AltLeft, 0, nil}
|
return Event{AltLeft, 0, nil}
|
||||||
}
|
}
|
||||||
if altShift {
|
if altShift {
|
||||||
return Event{AltSLeft, 0, nil}
|
return Event{AltShiftLeft, 0, nil}
|
||||||
}
|
}
|
||||||
return Event{SLeft, 0, nil}
|
return Event{ShiftLeft, 0, nil}
|
||||||
}
|
}
|
||||||
} // r.buffer[4]
|
} // r.buffer[4]
|
||||||
} // r.buffer[3]
|
} // r.buffer[3]
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func openTtyIn() *os.File {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprintln(os.Stderr, "Failed to open "+consoleDevice)
|
fmt.Fprintln(os.Stderr, "Failed to open "+consoleDevice)
|
||||||
os.Exit(2)
|
util.Exit(2)
|
||||||
}
|
}
|
||||||
return in
|
return in
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-19
@@ -320,16 +320,16 @@ func (r *FullscreenRenderer) GetChar() Event {
|
|||||||
switch ev.Rune() {
|
switch ev.Rune() {
|
||||||
case 0:
|
case 0:
|
||||||
if ctrl {
|
if ctrl {
|
||||||
return Event{BSpace, 0, nil}
|
return Event{Backspace, 0, nil}
|
||||||
}
|
}
|
||||||
case rune(tcell.KeyCtrlH):
|
case rune(tcell.KeyCtrlH):
|
||||||
switch {
|
switch {
|
||||||
case ctrl:
|
case ctrl:
|
||||||
return keyfn('h')
|
return keyfn('h')
|
||||||
case alt:
|
case alt:
|
||||||
return Event{AltBS, 0, nil}
|
return Event{AltBackspace, 0, nil}
|
||||||
case none, shift:
|
case none, shift:
|
||||||
return Event{BSpace, 0, nil}
|
return Event{Backspace, 0, nil}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case tcell.KeyCtrlI:
|
case tcell.KeyCtrlI:
|
||||||
@@ -382,17 +382,17 @@ func (r *FullscreenRenderer) GetChar() Event {
|
|||||||
// section 3: (Alt)+Backspace2
|
// section 3: (Alt)+Backspace2
|
||||||
case tcell.KeyBackspace2:
|
case tcell.KeyBackspace2:
|
||||||
if alt {
|
if alt {
|
||||||
return Event{AltBS, 0, nil}
|
return Event{AltBackspace, 0, nil}
|
||||||
}
|
}
|
||||||
return Event{BSpace, 0, nil}
|
return Event{Backspace, 0, nil}
|
||||||
|
|
||||||
// section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
|
// section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
|
||||||
case tcell.KeyUp:
|
case tcell.KeyUp:
|
||||||
if altShift {
|
if altShift {
|
||||||
return Event{AltSUp, 0, nil}
|
return Event{AltShiftUp, 0, nil}
|
||||||
}
|
}
|
||||||
if shift {
|
if shift {
|
||||||
return Event{SUp, 0, nil}
|
return Event{ShiftUp, 0, nil}
|
||||||
}
|
}
|
||||||
if alt {
|
if alt {
|
||||||
return Event{AltUp, 0, nil}
|
return Event{AltUp, 0, nil}
|
||||||
@@ -400,10 +400,10 @@ func (r *FullscreenRenderer) GetChar() Event {
|
|||||||
return Event{Up, 0, nil}
|
return Event{Up, 0, nil}
|
||||||
case tcell.KeyDown:
|
case tcell.KeyDown:
|
||||||
if altShift {
|
if altShift {
|
||||||
return Event{AltSDown, 0, nil}
|
return Event{AltShiftDown, 0, nil}
|
||||||
}
|
}
|
||||||
if shift {
|
if shift {
|
||||||
return Event{SDown, 0, nil}
|
return Event{ShiftDown, 0, nil}
|
||||||
}
|
}
|
||||||
if alt {
|
if alt {
|
||||||
return Event{AltDown, 0, nil}
|
return Event{AltDown, 0, nil}
|
||||||
@@ -411,10 +411,10 @@ func (r *FullscreenRenderer) GetChar() Event {
|
|||||||
return Event{Down, 0, nil}
|
return Event{Down, 0, nil}
|
||||||
case tcell.KeyLeft:
|
case tcell.KeyLeft:
|
||||||
if altShift {
|
if altShift {
|
||||||
return Event{AltSLeft, 0, nil}
|
return Event{AltShiftLeft, 0, nil}
|
||||||
}
|
}
|
||||||
if shift {
|
if shift {
|
||||||
return Event{SLeft, 0, nil}
|
return Event{ShiftLeft, 0, nil}
|
||||||
}
|
}
|
||||||
if alt {
|
if alt {
|
||||||
return Event{AltLeft, 0, nil}
|
return Event{AltLeft, 0, nil}
|
||||||
@@ -422,10 +422,10 @@ func (r *FullscreenRenderer) GetChar() Event {
|
|||||||
return Event{Left, 0, nil}
|
return Event{Left, 0, nil}
|
||||||
case tcell.KeyRight:
|
case tcell.KeyRight:
|
||||||
if altShift {
|
if altShift {
|
||||||
return Event{AltSRight, 0, nil}
|
return Event{AltShiftRight, 0, nil}
|
||||||
}
|
}
|
||||||
if shift {
|
if shift {
|
||||||
return Event{SRight, 0, nil}
|
return Event{ShiftRight, 0, nil}
|
||||||
}
|
}
|
||||||
if alt {
|
if alt {
|
||||||
return Event{AltRight, 0, nil}
|
return Event{AltRight, 0, nil}
|
||||||
@@ -442,17 +442,17 @@ func (r *FullscreenRenderer) GetChar() Event {
|
|||||||
return Event{CtrlDelete, 0, nil}
|
return Event{CtrlDelete, 0, nil}
|
||||||
}
|
}
|
||||||
if shift {
|
if shift {
|
||||||
return Event{SDelete, 0, nil}
|
return Event{ShiftDelete, 0, nil}
|
||||||
}
|
}
|
||||||
return Event{Del, 0, nil}
|
return Event{Delete, 0, nil}
|
||||||
case tcell.KeyEnd:
|
case tcell.KeyEnd:
|
||||||
return Event{End, 0, nil}
|
return Event{End, 0, nil}
|
||||||
case tcell.KeyPgUp:
|
case tcell.KeyPgUp:
|
||||||
return Event{PgUp, 0, nil}
|
return Event{PageUp, 0, nil}
|
||||||
case tcell.KeyPgDn:
|
case tcell.KeyPgDn:
|
||||||
return Event{PgDn, 0, nil}
|
return Event{PageDown, 0, nil}
|
||||||
case tcell.KeyBacktab:
|
case tcell.KeyBacktab:
|
||||||
return Event{BTab, 0, nil}
|
return Event{ShiftTab, 0, nil}
|
||||||
case tcell.KeyF1:
|
case tcell.KeyF1:
|
||||||
return Event{F1, 0, nil}
|
return Event{F1, 0, nil}
|
||||||
case tcell.KeyF2:
|
case tcell.KeyF2:
|
||||||
@@ -498,7 +498,7 @@ func (r *FullscreenRenderer) GetChar() Event {
|
|||||||
|
|
||||||
// section 7: Esc
|
// section 7: Esc
|
||||||
case tcell.KeyEsc:
|
case tcell.KeyEsc:
|
||||||
return Event{ESC, 0, nil}
|
return Event{Esc, 0, nil}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+25
-25
@@ -102,22 +102,22 @@ func TestGetCharEventKey(t *testing.T) {
|
|||||||
// KeyBackspace2 is alias for KeyDEL = 0x7F (ASCII) (allegedly unused by Windows)
|
// KeyBackspace2 is alias for KeyDEL = 0x7F (ASCII) (allegedly unused by Windows)
|
||||||
// KeyDelete = 0x2E (VK_DELETE constant in Windows)
|
// KeyDelete = 0x2E (VK_DELETE constant in Windows)
|
||||||
// KeyBackspace is alias for KeyBS = 0x08 (ASCII) (implicit alias with KeyCtrlH)
|
// KeyBackspace is alias for KeyBS = 0x08 (ASCII) (implicit alias with KeyCtrlH)
|
||||||
{giveKey{tcell.KeyBackspace2, 0, tcell.ModNone}, wantKey{BSpace, 0, nil}}, // fabricated
|
{giveKey{tcell.KeyBackspace2, 0, tcell.ModNone}, wantKey{Backspace, 0, nil}}, // fabricated
|
||||||
{giveKey{tcell.KeyBackspace2, 0, tcell.ModAlt}, wantKey{AltBS, 0, nil}}, // fabricated
|
{giveKey{tcell.KeyBackspace2, 0, tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // fabricated
|
||||||
{giveKey{tcell.KeyDEL, 0, tcell.ModNone}, wantKey{BSpace, 0, nil}}, // fabricated, unhandled
|
{giveKey{tcell.KeyDEL, 0, tcell.ModNone}, wantKey{Backspace, 0, nil}}, // fabricated, unhandled
|
||||||
{giveKey{tcell.KeyDelete, 0, tcell.ModNone}, wantKey{Del, 0, nil}},
|
{giveKey{tcell.KeyDelete, 0, tcell.ModNone}, wantKey{Delete, 0, nil}},
|
||||||
{giveKey{tcell.KeyDelete, 0, tcell.ModAlt}, wantKey{Del, 0, nil}},
|
{giveKey{tcell.KeyDelete, 0, tcell.ModAlt}, wantKey{Delete, 0, nil}},
|
||||||
{giveKey{tcell.KeyBackspace, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
|
{giveKey{tcell.KeyBackspace, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
|
||||||
{giveKey{tcell.KeyBS, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
|
{giveKey{tcell.KeyBS, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
|
||||||
{giveKey{tcell.KeyCtrlH, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
|
{giveKey{tcell.KeyCtrlH, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
|
||||||
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModNone}, wantKey{BSpace, 0, nil}}, // actual "Backspace" keystroke
|
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModNone}, wantKey{Backspace, 0, nil}}, // actual "Backspace" keystroke
|
||||||
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModAlt}, wantKey{AltBS, 0, nil}}, // actual "Alt+Backspace" keystroke
|
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Alt+Backspace" keystroke
|
||||||
{giveKey{tcell.KeyDEL, rune(tcell.KeyDEL), tcell.ModCtrl}, wantKey{BSpace, 0, nil}}, // actual "Ctrl+Backspace" keystroke
|
{giveKey{tcell.KeyDEL, rune(tcell.KeyDEL), tcell.ModCtrl}, wantKey{Backspace, 0, nil}}, // actual "Ctrl+Backspace" keystroke
|
||||||
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift}, wantKey{BSpace, 0, nil}}, // actual "Shift+Backspace" keystroke
|
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift}, wantKey{Backspace, 0, nil}}, // actual "Shift+Backspace" keystroke
|
||||||
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{BSpace, 0, nil}}, // actual "Ctrl+Alt+Backspace" keystroke
|
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{Backspace, 0, nil}}, // actual "Ctrl+Alt+Backspace" keystroke
|
||||||
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{BSpace, 0, nil}}, // actual "Ctrl+Shift+Backspace" keystroke
|
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{Backspace, 0, nil}}, // actual "Ctrl+Shift+Backspace" keystroke
|
||||||
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift | tcell.ModAlt}, wantKey{AltBS, 0, nil}}, // actual "Shift+Alt+Backspace" keystroke
|
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift | tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Shift+Alt+Backspace" keystroke
|
||||||
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{BSpace, 0, nil}}, // actual "Ctrl+Shift+Alt+Backspace" keystroke
|
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{Backspace, 0, nil}}, // actual "Ctrl+Shift+Alt+Backspace" keystroke
|
||||||
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+H" keystroke
|
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+H" keystroke
|
||||||
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAlt, 'h', nil}}, // fabricated "Ctrl+Alt+H" keystroke
|
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAlt, 'h', nil}}, // fabricated "Ctrl+Alt+H" keystroke
|
||||||
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+Shift+H" keystroke
|
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+Shift+H" keystroke
|
||||||
@@ -126,8 +126,8 @@ func TestGetCharEventKey(t *testing.T) {
|
|||||||
// section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
|
// section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
|
||||||
{giveKey{tcell.KeyUp, 0, tcell.ModNone}, wantKey{Up, 0, nil}},
|
{giveKey{tcell.KeyUp, 0, tcell.ModNone}, wantKey{Up, 0, nil}},
|
||||||
{giveKey{tcell.KeyDown, 0, tcell.ModAlt}, wantKey{AltDown, 0, nil}},
|
{giveKey{tcell.KeyDown, 0, tcell.ModAlt}, wantKey{AltDown, 0, nil}},
|
||||||
{giveKey{tcell.KeyLeft, 0, tcell.ModShift}, wantKey{SLeft, 0, nil}},
|
{giveKey{tcell.KeyLeft, 0, tcell.ModShift}, wantKey{ShiftLeft, 0, nil}},
|
||||||
{giveKey{tcell.KeyRight, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltSRight, 0, nil}},
|
{giveKey{tcell.KeyRight, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltShiftRight, 0, nil}},
|
||||||
{giveKey{tcell.KeyUpLeft, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
|
{giveKey{tcell.KeyUpLeft, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
|
||||||
{giveKey{tcell.KeyUpRight, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
|
{giveKey{tcell.KeyUpRight, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
|
||||||
{giveKey{tcell.KeyDownLeft, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
|
{giveKey{tcell.KeyDownLeft, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
|
||||||
@@ -161,11 +161,11 @@ func TestGetCharEventKey(t *testing.T) {
|
|||||||
|
|
||||||
// section 7: Esc
|
// section 7: Esc
|
||||||
// KeyEsc and KeyEscape are aliases for KeyESC
|
// KeyEsc and KeyEscape are aliases for KeyESC
|
||||||
{giveKey{tcell.KeyEsc, rune(tcell.KeyEsc), tcell.ModNone}, wantKey{ESC, 0, nil}}, // fabricated
|
{giveKey{tcell.KeyEsc, rune(tcell.KeyEsc), tcell.ModNone}, wantKey{Esc, 0, nil}}, // fabricated
|
||||||
{giveKey{tcell.KeyESC, rune(tcell.KeyESC), tcell.ModNone}, wantKey{ESC, 0, nil}}, // unhandled
|
{giveKey{tcell.KeyESC, rune(tcell.KeyESC), tcell.ModNone}, wantKey{Esc, 0, nil}}, // unhandled
|
||||||
{giveKey{tcell.KeyEscape, rune(tcell.KeyEscape), tcell.ModNone}, wantKey{ESC, 0, nil}}, // fabricated, unhandled
|
{giveKey{tcell.KeyEscape, rune(tcell.KeyEscape), tcell.ModNone}, wantKey{Esc, 0, nil}}, // fabricated, unhandled
|
||||||
{giveKey{tcell.KeyESC, rune(tcell.KeyESC), tcell.ModCtrl}, wantKey{ESC, 0, nil}}, // actual Ctrl+[ keystroke
|
{giveKey{tcell.KeyESC, rune(tcell.KeyESC), tcell.ModCtrl}, wantKey{Esc, 0, nil}}, // actual Ctrl+[ keystroke
|
||||||
{giveKey{tcell.KeyCtrlLeftSq, rune(tcell.KeyCtrlLeftSq), tcell.ModCtrl}, wantKey{ESC, 0, nil}}, // fabricated, unhandled
|
{giveKey{tcell.KeyCtrlLeftSq, rune(tcell.KeyCtrlLeftSq), tcell.ModCtrl}, wantKey{Esc, 0, nil}}, // fabricated, unhandled
|
||||||
|
|
||||||
// section 8: Invalid
|
// section 8: Invalid
|
||||||
{giveKey{tcell.KeyRune, 'a', tcell.ModMeta}, wantKey{Rune, 'a', nil}}, // fabricated
|
{giveKey{tcell.KeyRune, 'a', tcell.ModMeta}, wantKey{Rune, 'a', nil}}, // fabricated
|
||||||
@@ -259,7 +259,7 @@ Quick reference
|
|||||||
37 LeftClick
|
37 LeftClick
|
||||||
38 RightClick
|
38 RightClick
|
||||||
39 BTab
|
39 BTab
|
||||||
40 BSpace
|
40 Backspace
|
||||||
41 Del
|
41 Del
|
||||||
42 PgUp
|
42 PgUp
|
||||||
43 PgDn
|
43 PgDn
|
||||||
@@ -272,7 +272,7 @@ Quick reference
|
|||||||
50 Insert
|
50 Insert
|
||||||
51 SUp
|
51 SUp
|
||||||
52 SDown
|
52 SDown
|
||||||
53 SLeft
|
53 ShiftLeft
|
||||||
54 SRight
|
54 SRight
|
||||||
55 F1
|
55 F1
|
||||||
56 F2
|
56 F2
|
||||||
@@ -288,15 +288,15 @@ Quick reference
|
|||||||
66 F12
|
66 F12
|
||||||
67 Change
|
67 Change
|
||||||
68 BackwardEOF
|
68 BackwardEOF
|
||||||
69 AltBS
|
69 AltBackspace
|
||||||
70 AltUp
|
70 AltUp
|
||||||
71 AltDown
|
71 AltDown
|
||||||
72 AltLeft
|
72 AltLeft
|
||||||
73 AltRight
|
73 AltRight
|
||||||
74 AltSUp
|
74 AltSUp
|
||||||
75 AltSDown
|
75 AltSDown
|
||||||
76 AltSLeft
|
76 AltShiftLeft
|
||||||
77 AltSRight
|
77 AltShiftRight
|
||||||
78 Alt
|
78 Alt
|
||||||
79 CtrlAlt
|
79 CtrlAlt
|
||||||
..
|
..
|
||||||
|
|||||||
+74
-42
@@ -6,10 +6,13 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/junegunn/fzf/src/util"
|
||||||
"github.com/rivo/uniseg"
|
"github.com/rivo/uniseg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Types of user action
|
// Types of user action
|
||||||
|
//
|
||||||
|
//go:generate stringer -type=EventType
|
||||||
type EventType int
|
type EventType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -41,7 +44,7 @@ const (
|
|||||||
CtrlX
|
CtrlX
|
||||||
CtrlY
|
CtrlY
|
||||||
CtrlZ
|
CtrlZ
|
||||||
ESC
|
Esc
|
||||||
CtrlSpace
|
CtrlSpace
|
||||||
CtrlDelete
|
CtrlDelete
|
||||||
|
|
||||||
@@ -51,27 +54,12 @@ const (
|
|||||||
CtrlCaret
|
CtrlCaret
|
||||||
CtrlSlash
|
CtrlSlash
|
||||||
|
|
||||||
Invalid
|
ShiftTab
|
||||||
Resize
|
Backspace
|
||||||
Mouse
|
|
||||||
DoubleClick
|
|
||||||
LeftClick
|
|
||||||
RightClick
|
|
||||||
SLeftClick
|
|
||||||
SRightClick
|
|
||||||
ScrollUp
|
|
||||||
ScrollDown
|
|
||||||
SScrollUp
|
|
||||||
SScrollDown
|
|
||||||
PreviewScrollUp
|
|
||||||
PreviewScrollDown
|
|
||||||
|
|
||||||
BTab
|
Delete
|
||||||
BSpace
|
PageUp
|
||||||
|
PageDown
|
||||||
Del
|
|
||||||
PgUp
|
|
||||||
PgDn
|
|
||||||
|
|
||||||
Up
|
Up
|
||||||
Down
|
Down
|
||||||
@@ -81,11 +69,11 @@ const (
|
|||||||
End
|
End
|
||||||
Insert
|
Insert
|
||||||
|
|
||||||
SUp
|
ShiftUp
|
||||||
SDown
|
ShiftDown
|
||||||
SLeft
|
ShiftLeft
|
||||||
SRight
|
ShiftRight
|
||||||
SDelete
|
ShiftDelete
|
||||||
|
|
||||||
F1
|
F1
|
||||||
F2
|
F2
|
||||||
@@ -100,6 +88,38 @@ const (
|
|||||||
F11
|
F11
|
||||||
F12
|
F12
|
||||||
|
|
||||||
|
AltBackspace
|
||||||
|
|
||||||
|
AltUp
|
||||||
|
AltDown
|
||||||
|
AltLeft
|
||||||
|
AltRight
|
||||||
|
|
||||||
|
AltShiftUp
|
||||||
|
AltShiftDown
|
||||||
|
AltShiftLeft
|
||||||
|
AltShiftRight
|
||||||
|
|
||||||
|
Alt
|
||||||
|
CtrlAlt
|
||||||
|
|
||||||
|
Invalid
|
||||||
|
|
||||||
|
Mouse
|
||||||
|
DoubleClick
|
||||||
|
LeftClick
|
||||||
|
RightClick
|
||||||
|
SLeftClick
|
||||||
|
SRightClick
|
||||||
|
ScrollUp
|
||||||
|
ScrollDown
|
||||||
|
SScrollUp
|
||||||
|
SScrollDown
|
||||||
|
PreviewScrollUp
|
||||||
|
PreviewScrollDown
|
||||||
|
|
||||||
|
// Events
|
||||||
|
Resize
|
||||||
Change
|
Change
|
||||||
BackwardEOF
|
BackwardEOF
|
||||||
Start
|
Start
|
||||||
@@ -108,21 +128,8 @@ const (
|
|||||||
One
|
One
|
||||||
Zero
|
Zero
|
||||||
Result
|
Result
|
||||||
|
Jump
|
||||||
AltBS
|
JumpCancel
|
||||||
|
|
||||||
AltUp
|
|
||||||
AltDown
|
|
||||||
AltLeft
|
|
||||||
AltRight
|
|
||||||
|
|
||||||
AltSUp
|
|
||||||
AltSDown
|
|
||||||
AltSLeft
|
|
||||||
AltSRight
|
|
||||||
|
|
||||||
Alt
|
|
||||||
CtrlAlt
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t EventType) AsEvent() Event {
|
func (t EventType) AsEvent() Event {
|
||||||
@@ -142,6 +149,31 @@ func (e Event) Comparable() Event {
|
|||||||
return Event{e.Type, e.Char, nil}
|
return Event{e.Type, e.Char, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e Event) KeyName() string {
|
||||||
|
if e.Type >= Invalid {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.Type {
|
||||||
|
case Rune:
|
||||||
|
return string(e.Char)
|
||||||
|
case Alt:
|
||||||
|
return "alt-" + string(e.Char)
|
||||||
|
case CtrlAlt:
|
||||||
|
return "ctrl-alt-" + string(e.Char)
|
||||||
|
case CtrlBackSlash:
|
||||||
|
return "ctrl-\\"
|
||||||
|
case CtrlRightBracket:
|
||||||
|
return "ctrl-]"
|
||||||
|
case CtrlCaret:
|
||||||
|
return "ctrl-^"
|
||||||
|
case CtrlSlash:
|
||||||
|
return "ctrl-/"
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.ToKebabCase(e.Type.String())
|
||||||
|
}
|
||||||
|
|
||||||
func Key(r rune) Event {
|
func Key(r rune) Event {
|
||||||
return Event{Rune, r, nil}
|
return Event{Rune, r, nil}
|
||||||
}
|
}
|
||||||
@@ -646,7 +678,7 @@ func NoColorTheme() *ColorTheme {
|
|||||||
|
|
||||||
func errorExit(message string) {
|
func errorExit(message string) {
|
||||||
fmt.Fprintln(os.Stderr, message)
|
fmt.Fprintln(os.Stderr, message)
|
||||||
os.Exit(2)
|
util.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var atExitFuncs []func()
|
||||||
|
|
||||||
|
// AtExit registers the function fn to be called on program termination.
|
||||||
|
// The functions will be called in reverse order they were registered.
|
||||||
|
func AtExit(fn func()) {
|
||||||
|
if fn == nil {
|
||||||
|
panic("AtExit called with nil func")
|
||||||
|
}
|
||||||
|
once := &sync.Once{}
|
||||||
|
atExitFuncs = append(atExitFuncs, func() {
|
||||||
|
once.Do(fn)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunAtExitFuncs runs any functions registered with AtExit().
|
||||||
|
func RunAtExitFuncs() {
|
||||||
|
fns := atExitFuncs
|
||||||
|
for i := len(fns) - 1; i >= 0; i-- {
|
||||||
|
fns[i]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit executes any functions registered with AtExit() then exits the program
|
||||||
|
// with os.Exit(code).
|
||||||
|
//
|
||||||
|
// NOTE: It must be used instead of os.Exit() since calling os.Exit() terminates
|
||||||
|
// the program before any of the AtExit functions can run.
|
||||||
|
func Exit(code int) {
|
||||||
|
defer os.Exit(code)
|
||||||
|
RunAtExitFuncs()
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAtExit(t *testing.T) {
|
||||||
|
want := []int{3, 2, 1, 0}
|
||||||
|
var called []int
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
n := i
|
||||||
|
AtExit(func() { called = append(called, n) })
|
||||||
|
}
|
||||||
|
RunAtExitFuncs()
|
||||||
|
if !reflect.DeepEqual(called, want) {
|
||||||
|
t.Errorf("AtExit: want call order: %v got: %v", want, called)
|
||||||
|
}
|
||||||
|
|
||||||
|
RunAtExitFuncs()
|
||||||
|
if !reflect.DeepEqual(called, want) {
|
||||||
|
t.Error("AtExit: should only call exit funcs once")
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -163,7 +163,7 @@ func (chars *Chars) ToString() string {
|
|||||||
if runes := chars.optionalRunes(); runes != nil {
|
if runes := chars.optionalRunes(); runes != nil {
|
||||||
return string(runes)
|
return string(runes)
|
||||||
}
|
}
|
||||||
return string(chars.slice)
|
return unsafe.String(unsafe.SliceData(chars.slice), len(chars.slice))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (chars *Chars) ToRunes() []rune {
|
func (chars *Chars) ToRunes() []rune {
|
||||||
@@ -178,12 +178,12 @@ func (chars *Chars) ToRunes() []rune {
|
|||||||
return runes
|
return runes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (chars *Chars) CopyRunes(dest []rune) {
|
func (chars *Chars) CopyRunes(dest []rune, from int) {
|
||||||
if runes := chars.optionalRunes(); runes != nil {
|
if runes := chars.optionalRunes(); runes != nil {
|
||||||
copy(dest, runes)
|
copy(dest, runes[from:])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for idx, b := range chars.slice[:len(dest)] {
|
for idx, b := range chars.slice[from:][:len(dest)] {
|
||||||
dest[idx] = rune(b)
|
dest[idx] = rune(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,3 +176,15 @@ func RepeatToFill(str string, length int, limit int) string {
|
|||||||
}
|
}
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToKebabCase converts the given CamelCase string to kebab-case
|
||||||
|
func ToKebabCase(s string) string {
|
||||||
|
name := ""
|
||||||
|
for i, r := range s {
|
||||||
|
if i > 0 && r >= 'A' && r <= 'Z' {
|
||||||
|
name += "-"
|
||||||
|
}
|
||||||
|
name += string(r)
|
||||||
|
}
|
||||||
|
return strings.ToLower(name)
|
||||||
|
}
|
||||||
|
|||||||
+122
-8
@@ -18,7 +18,6 @@ UNSETS = %w[
|
|||||||
FZF_ALT_C_COMMAND
|
FZF_ALT_C_COMMAND
|
||||||
FZF_ALT_C_OPTS FZF_CTRL_R_OPTS
|
FZF_ALT_C_OPTS FZF_CTRL_R_OPTS
|
||||||
FZF_API_KEY
|
FZF_API_KEY
|
||||||
fish_history
|
|
||||||
].freeze
|
].freeze
|
||||||
DEFAULT_TIMEOUT = 10
|
DEFAULT_TIMEOUT = 10
|
||||||
|
|
||||||
@@ -67,7 +66,7 @@ class Shell
|
|||||||
end
|
end
|
||||||
|
|
||||||
def fish
|
def fish
|
||||||
UNSETS.map { |v| v + '= ' }.join + ' FZF_DEFAULT_OPTS=--no-scrollbar fish'
|
"unset #{UNSETS.join(' ')}; FZF_DEFAULT_OPTS=--no-scrollbar fish_history= fish"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1469,6 +1468,19 @@ class TestGoFZF < TestBase
|
|||||||
assert_equal '3', readonce.chomp
|
assert_equal '3', readonce.chomp
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_jump_events
|
||||||
|
tmux.send_keys "seq 1000 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump,jump:preview(echo jumped to {}),jump-cancel:preview(echo jump cancelled at {})'")}", :Enter
|
||||||
|
tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
|
||||||
|
tmux.send_keys 'C-j'
|
||||||
|
tmux.until { |lines| assert_includes lines[-7], '5 5' }
|
||||||
|
tmux.send_keys '3'
|
||||||
|
tmux.until { |lines| assert(lines.any? { _1.include?('jumped to 3') }) }
|
||||||
|
tmux.send_keys 'C-j'
|
||||||
|
tmux.until { |lines| assert_includes lines[-7], '5 5' }
|
||||||
|
tmux.send_keys 'C-c'
|
||||||
|
tmux.until { |lines| assert(lines.any? { _1.include?('jump cancelled at 3') }) }
|
||||||
|
end
|
||||||
|
|
||||||
def test_pointer
|
def test_pointer
|
||||||
tmux.send_keys "seq 10 | #{fzf("--pointer '>>'")}", :Enter
|
tmux.send_keys "seq 10 | #{fzf("--pointer '>>'")}", :Enter
|
||||||
# Assert that specified pointer is displayed
|
# Assert that specified pointer is displayed
|
||||||
@@ -1720,7 +1732,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_info_hidden
|
def test_info_hidden
|
||||||
tmux.send_keys 'seq 10 | fzf --info=hidden', :Enter
|
tmux.send_keys 'seq 10 | fzf --info=hidden --no-separator', :Enter
|
||||||
tmux.until { |lines| assert_equal '> 1', lines[-2] }
|
tmux.until { |lines| assert_equal '> 1', lines[-2] }
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2464,6 +2476,84 @@ class TestGoFZF < TestBase
|
|||||||
tmux.until { |lines| assert_equal 10, lines.match_count }
|
tmux.until { |lines| assert_equal 10, lines.match_count }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_reload_disabled_case1
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload(sleep 2; seq 1000)'", :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 100, lines.item_count
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { |lines| assert_equal 0, lines.match_count }
|
||||||
|
tmux.until { |lines| assert_equal 1000, lines.match_count }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_reload_disabled_case2
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload-sync(sleep 2; seq 1000)'", :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 100, lines.item_count
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.until { |lines| assert_equal 1000, lines.match_count }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_reload_disabled_case3
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload(sleep 2; seq 1000)+backward-delete-char'", :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 100, lines.item_count
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { |lines| assert_equal 0, lines.match_count }
|
||||||
|
tmux.until { |lines| assert_equal 1000, lines.match_count }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_reload_disabled_case4
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload-sync(sleep 2; seq 1000)+backward-delete-char'", :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 100, lines.item_count
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
tmux.until { |lines| assert_equal 1000, lines.match_count }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_reload_disabled_case5
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --query 99 --bind 'space:disable-search+reload(echo xx; sleep 2; seq 1000)'", :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 100, lines.item_count
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 1, lines.item_count
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { |lines| assert_equal 1001, lines.match_count }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_reload_disabled_case6
|
||||||
|
tmux.send_keys "seq 1000 | #{FZF} --disabled --bind 'change:reload:sleep 0.5; seq {q}'", :Enter
|
||||||
|
tmux.until { |lines| assert_equal 1000, lines.match_count }
|
||||||
|
tmux.send_keys '9'
|
||||||
|
tmux.until { |lines| assert_equal 9, lines.match_count }
|
||||||
|
tmux.send_keys '9'
|
||||||
|
tmux.until { |lines| assert_equal 99, lines.match_count }
|
||||||
|
|
||||||
|
# TODO: How do we verify if an intermediate empty list is not shown?
|
||||||
|
end
|
||||||
|
|
||||||
def test_scroll_off
|
def test_scroll_off
|
||||||
tmux.send_keys "seq 1000 | #{FZF} --scroll-off=3 --bind l:last", :Enter
|
tmux.send_keys "seq 1000 | #{FZF} --scroll-off=3 --bind l:last", :Enter
|
||||||
tmux.until { |lines| assert_equal 1000, lines.item_count }
|
tmux.until { |lines| assert_equal 1000, lines.item_count }
|
||||||
@@ -2800,6 +2890,27 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_labels_variables
|
||||||
|
tmux.send_keys ': | fzf --border --border-label foobar --preview "echo \$FZF_BORDER_LABEL // \$FZF_PREVIEW_LABEL" --preview-label barfoo --bind "space:change-border-label(barbaz)+change-preview-label(bazbar)+refresh-preview,enter:transform-border-label(echo 123)+transform-preview-label(echo 456)+refresh-preview"', :Enter
|
||||||
|
tmux.until do
|
||||||
|
assert_includes(_1[0], '─foobar─')
|
||||||
|
assert_includes(_1[1], '─barfoo─')
|
||||||
|
assert_includes(_1[2], ' foobar // barfoo ')
|
||||||
|
end
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until do
|
||||||
|
assert_includes(_1[0], '─barbaz─')
|
||||||
|
assert_includes(_1[1], '─bazbar─')
|
||||||
|
assert_includes(_1[2], ' barbaz // bazbar ')
|
||||||
|
end
|
||||||
|
tmux.send_keys :Enter
|
||||||
|
tmux.until do
|
||||||
|
assert_includes(_1[0], '─123─')
|
||||||
|
assert_includes(_1[1], '─456─')
|
||||||
|
assert_includes(_1[2], ' 123 // 456 ')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_info_separator_unicode
|
def test_info_separator_unicode
|
||||||
tmux.send_keys 'seq 100 | fzf -q55', :Enter
|
tmux.send_keys 'seq 100 | fzf -q55', :Enter
|
||||||
tmux.until { assert_includes(_1[-2], ' 1/100 ─') }
|
tmux.until { assert_includes(_1[-2], ' 1/100 ─') }
|
||||||
@@ -3003,7 +3114,7 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
tmux.send_keys :t
|
tmux.send_keys :t
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
assert_includes lines[-2], '+T'
|
assert_includes lines[-2], '+t'
|
||||||
end
|
end
|
||||||
tmux.send_keys :BSpace
|
tmux.send_keys :BSpace
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
@@ -3015,7 +3126,7 @@ class TestGoFZF < TestBase
|
|||||||
tmux.send_keys '4'
|
tmux.send_keys '4'
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
assert_equal 28, lines.match_count
|
assert_equal 28, lines.match_count
|
||||||
refute_includes lines[-2], '+T'
|
refute_includes lines[-2], '+t'
|
||||||
end
|
end
|
||||||
tmux.send_keys :BSpace
|
tmux.send_keys :BSpace
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
@@ -3024,11 +3135,11 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
tmux.send_keys :t
|
tmux.send_keys :t
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
assert_includes lines[-2], '+T'
|
assert_includes lines[-2], '+t'
|
||||||
end
|
end
|
||||||
tmux.send_keys :Up
|
tmux.send_keys :Up
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
refute_includes lines[-2], '+T'
|
refute_includes lines[-2], '+t'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -3308,7 +3419,10 @@ module CompletionTest
|
|||||||
tmux.send_keys 'cat /tmp/fzf\ test/**', :Tab
|
tmux.send_keys 'cat /tmp/fzf\ test/**', :Tab
|
||||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||||
tmux.send_keys 'foobar$'
|
tmux.send_keys 'foobar$'
|
||||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
tmux.until do |lines|
|
||||||
|
assert_equal 1, lines.match_count
|
||||||
|
assert lines.any_include?('> /tmp/fzf test/foobar')
|
||||||
|
end
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.until(true) { |lines| assert_equal 'cat /tmp/fzf\ test/foobar', lines[-1] }
|
tmux.until(true) { |lines| assert_equal 'cat /tmp/fzf\ test/foobar', lines[-1] }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user