mirror of
https://github.com/junegunn/fzf.git
synced 2025-08-01 04:32:05 -07:00
Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
04d0b0223f | ||
|
78ad6d2d88 | ||
|
22cbd9fa58 | ||
|
984049586a | ||
|
cdfc2b92e3 | ||
|
4530abe8df | ||
|
586020b8b6 | ||
|
3a8626fd04 | ||
|
a6e483a434 | ||
|
6a942e56b1 | ||
|
87c91550ad | ||
|
731daf0f37 | ||
|
f931e53890 | ||
|
b5efc68737 | ||
|
b9e6e7926c | ||
|
845034c81c | ||
|
54d42e3f40 |
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -27,18 +27,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
|
||||
uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@1ed1437484560351c5be56cf73a48a279d116b78
|
||||
uses: github/codeql-action/init@86f3159a697a097a813ad9bfa0002412d97690a4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@1ed1437484560351c5be56cf73a48a279d116b78
|
||||
uses: github/codeql-action/autobuild@86f3159a697a097a813ad9bfa0002412d97690a4
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@1ed1437484560351c5be56cf73a48a279d116b78
|
||||
uses: github/codeql-action/analyze@86f3159a697a097a813ad9bfa0002412d97690a4
|
||||
|
6
.github/workflows/linux.yml
vendored
6
.github/workflows/linux.yml
vendored
@@ -15,17 +15,17 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
|
||||
- uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@f6164bd8c8acb4a71fb2791a8b6c4024ff038dab # v2
|
||||
uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f # v2
|
||||
with:
|
||||
go-version: 1.19
|
||||
|
||||
- name: Setup Ruby
|
||||
uses: ruby/setup-ruby@ebaea52cb20fea395b0904125276395e37183dac
|
||||
uses: ruby/setup-ruby@3068fa83f9cbd7ae106cac45483635a2f3a195c9
|
||||
with:
|
||||
ruby-version: 3.0.0
|
||||
|
||||
|
6
.github/workflows/macos.yml
vendored
6
.github/workflows/macos.yml
vendored
@@ -15,17 +15,17 @@ jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
|
||||
- uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@f6164bd8c8acb4a71fb2791a8b6c4024ff038dab # v2
|
||||
uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f # v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Setup Ruby
|
||||
uses: ruby/setup-ruby@ebaea52cb20fea395b0904125276395e37183dac
|
||||
uses: ruby/setup-ruby@3068fa83f9cbd7ae106cac45483635a2f3a195c9
|
||||
with:
|
||||
ruby-version: 3.0.0
|
||||
|
||||
|
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,13 +1,44 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
0.34.0
|
||||
------
|
||||
- Added support for adaptive `--height`. If the `--height` value is prefixed
|
||||
with `~`, fzf will automatically determine the height in the range according
|
||||
to the input size.
|
||||
```sh
|
||||
seq 1 | fzf --height ~70% --border --padding 1 --margin 1
|
||||
seq 10 | fzf --height ~70% --border --padding 1 --margin 1
|
||||
seq 100 | fzf --height ~70% --border --padding 1 --margin 1
|
||||
```
|
||||
- There are a few limitations
|
||||
- Not compatible with percent top/bottom margin/padding
|
||||
```sh
|
||||
# This is not allowed (top/bottom margin in percent value)
|
||||
fzf --height ~50% --border --margin 5%,10%
|
||||
|
||||
# This is allowed (top/bottom margin in fixed value)
|
||||
fzf --height ~50% --border --margin 2,10%
|
||||
```
|
||||
- fzf will not start until it can determine the right height for the input
|
||||
```sh
|
||||
# fzf will open immediately
|
||||
(sleep 2; seq 10) | fzf --height 50%
|
||||
|
||||
# fzf will open after 2 seconds
|
||||
(sleep 2; seq 10) | fzf --height ~50%
|
||||
(sleep 2; seq 1000) | fzf --height ~50%
|
||||
```
|
||||
- Fixed tcell renderer used to render full-screen fzf on Windows
|
||||
- `--no-clear` is deprecated. Use `reload` action instead.
|
||||
|
||||
0.33.0
|
||||
------
|
||||
- Added `--scheme=[default|path|history]` option to choose scoring scheme
|
||||
- (Experimental)
|
||||
- We updated the scoring algorithm in 0.32.0, however we have learned that
|
||||
this new scheme (`default`) is not always giving the optimal result
|
||||
- `path`: Additional bonus point is only given the the characters after
|
||||
- `path`: Additional bonus point is only given to the characters after
|
||||
path separator. You might want to choose this scheme if you have many
|
||||
files with spaces in their paths.
|
||||
- `history`: No additional bonus points are given so that we give more
|
||||
|
1
Makefile
1
Makefile
@@ -155,6 +155,7 @@ target/$(BINARYLOONG64): $(SOURCES)
|
||||
GOARCH=loong64 $(GO) build $(BUILD_FLAGS) -o $@
|
||||
|
||||
bin/fzf: target/$(BINARY) | bin
|
||||
-rm -f bin/fzf
|
||||
cp -f target/$(BINARY) bin/fzf
|
||||
|
||||
docker:
|
||||
|
12
go.mod
12
go.mod
@@ -2,19 +2,19 @@ module github.com/junegunn/fzf
|
||||
|
||||
require (
|
||||
github.com/gdamore/tcell/v2 v2.5.3
|
||||
github.com/mattn/go-isatty v0.0.14
|
||||
github.com/mattn/go-runewidth v0.0.13
|
||||
github.com/mattn/go-isatty v0.0.16
|
||||
github.com/mattn/go-runewidth v0.0.14
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/rivo/uniseg v0.2.0
|
||||
github.com/saracen/walker v0.1.2
|
||||
golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12
|
||||
github.com/rivo/uniseg v0.4.2
|
||||
github.com/saracen/walker v0.1.3
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gdamore/encoding v1.0.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
)
|
||||
|
||||
|
24
go.sum
24
go.sum
@@ -4,25 +4,25 @@ github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV
|
||||
github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/saracen/walker v0.1.2 h1:/o1TxP82n8thLvmL4GpJXduYaRmJ7qXp8u9dSlV0zmo=
|
||||
github.com/saracen/walker v0.1.2/go.mod h1:0oKYMsKVhSJ+ful4p/XbjvXbMgLEkLITZaxozsl4CGE=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
|
||||
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/saracen/walker v0.1.3 h1:YtcKKmpRPy6XJTHJ75J2QYXXZYWnZNQxPCVqZSHVV/g=
|
||||
github.com/saracen/walker v0.1.3/go.mod h1:FU+7qU8DeQQgSZDmmThMJi93kPkLFgy0oVAcLxurjIk=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12 h1:QyVthZKMsyaQwBTJE04jdNN0Pp5Fn9Qga0mrgxyERQM=
|
||||
golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
2
install
2
install
@@ -2,7 +2,7 @@
|
||||
|
||||
set -u
|
||||
|
||||
version=0.33.0
|
||||
version=0.34.0
|
||||
auto_completion=
|
||||
key_bindings=
|
||||
update_config=2
|
||||
|
@@ -1,4 +1,4 @@
|
||||
$version="0.33.0"
|
||||
$version="0.34.0"
|
||||
|
||||
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
|
||||
|
2
main.go
2
main.go
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/junegunn/fzf/src/protector"
|
||||
)
|
||||
|
||||
var version string = "0.33"
|
||||
var version string = "0.34"
|
||||
var revision string = "devel"
|
||||
|
||||
func main() {
|
||||
|
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
..
|
||||
.TH fzf-tmux 1 "Aug 2022" "fzf 0.33.0" "fzf-tmux - open fzf in tmux split pane"
|
||||
.TH fzf-tmux 1 "Sep 2022" "fzf 0.34.0" "fzf-tmux - open fzf in tmux split pane"
|
||||
|
||||
.SH NAME
|
||||
fzf-tmux - open fzf in tmux split pane
|
||||
|
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
..
|
||||
.TH fzf 1 "Aug 2022" "fzf 0.33.0" "fzf - a command-line fuzzy finder"
|
||||
.TH fzf 1 "Sep 2022" "fzf 0.34.0" "fzf - a command-line fuzzy finder"
|
||||
|
||||
.SH NAME
|
||||
fzf - a command-line fuzzy finder
|
||||
@@ -177,9 +177,11 @@ actions are affected:
|
||||
Label characters for \fBjump\fR and \fBjump-accept\fR
|
||||
.SS Layout
|
||||
.TP
|
||||
.BI "--height=" "HEIGHT[%]"
|
||||
.BI "--height=" "[~]HEIGHT[%]"
|
||||
Display fzf window below the cursor with the given height instead of using
|
||||
the full screen.
|
||||
the full screen. When prefixed with \fB~\fR, fzf will automatically determine
|
||||
the height in the range according to the input size. Note that adaptive height
|
||||
is not compatible with top/bottom margin and padding given in percent size.
|
||||
.TP
|
||||
.BI "--min-height=" "HEIGHT"
|
||||
Minimum height when \fB--height\fR is given in percent (default: 10).
|
||||
@@ -616,12 +618,6 @@ Read input delimited by ASCII NUL characters instead of newline characters
|
||||
.B "--print0"
|
||||
Print output delimited by ASCII NUL characters instead of newline characters
|
||||
.TP
|
||||
.B "--no-clear"
|
||||
Do not clear finder interface on exit. If fzf was started in full screen mode,
|
||||
it will not switch back to the original screen, so you'll have to manually run
|
||||
\fBtput rmcup\fR to return. This option can be used to avoid flickering of the
|
||||
screen when your application needs to start fzf multiple times in order.
|
||||
.TP
|
||||
.B "--sync"
|
||||
Synchronous search for multi-staged filtering. If specified, fzf will launch
|
||||
ncurses finder only after the input stream is complete.
|
||||
|
@@ -288,13 +288,13 @@ _fzf_host_completion() {
|
||||
|
||||
_fzf_var_completion() {
|
||||
_fzf_complete -m -- "$@" < <(
|
||||
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
||||
declare -xp | sed -En 's|^declare [^ ]+ ([^=]+).*|\1|p'
|
||||
)
|
||||
}
|
||||
|
||||
_fzf_alias_completion() {
|
||||
_fzf_complete -m -- "$@" < <(
|
||||
alias | sed 's/=.*//' | sed 's/.* //'
|
||||
alias | sed -En 's|^alias ([^=]+).*|\1|p'
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -87,7 +87,7 @@ function fzf_key_bindings
|
||||
eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)' +m --query "'$fzf_query'"' | read -l result
|
||||
|
||||
if [ -n "$result" ]
|
||||
builtin cd -- $result
|
||||
cd -- $result
|
||||
|
||||
# Remove last token from commandline.
|
||||
commandline -t ""
|
||||
|
35
src/core.go
35
src/core.go
@@ -194,10 +194,17 @@ func Run(opts *Options, version string, revision string) {
|
||||
|
||||
// Terminal I/O
|
||||
terminal := NewTerminal(opts, eventBox)
|
||||
maxFit := 0 // Maximum number of items that can fit on screen
|
||||
padHeight := 0
|
||||
heightUnknown := opts.Height.auto
|
||||
if heightUnknown {
|
||||
maxFit, padHeight = terminal.MaxFitAndPad(opts)
|
||||
}
|
||||
deferred := opts.Select1 || opts.Exit0
|
||||
go terminal.Loop()
|
||||
if !deferred {
|
||||
terminal.startChan <- true
|
||||
if !deferred && !heightUnknown {
|
||||
// Start right away
|
||||
terminal.startChan <- fitpad{-1, -1}
|
||||
}
|
||||
|
||||
// Event coordination
|
||||
@@ -216,7 +223,19 @@ func Run(opts *Options, version string, revision string) {
|
||||
go reader.restart(command)
|
||||
}
|
||||
eventBox.Watch(EvtReadNew)
|
||||
total := 0
|
||||
query := []rune{}
|
||||
determine := func(final bool) {
|
||||
if heightUnknown {
|
||||
if total >= maxFit || final {
|
||||
heightUnknown = false
|
||||
terminal.startChan <- fitpad{util.Min(total, maxFit), padHeight}
|
||||
}
|
||||
} else if deferred {
|
||||
deferred = false
|
||||
terminal.startChan <- fitpad{-1, -1}
|
||||
}
|
||||
}
|
||||
for {
|
||||
delay := true
|
||||
ticks++
|
||||
@@ -249,11 +268,15 @@ func Run(opts *Options, version string, revision string) {
|
||||
reading = reading && evt == EvtReadNew
|
||||
}
|
||||
snapshot, count := chunkList.Snapshot()
|
||||
terminal.UpdateCount(count, !reading, value.(*string))
|
||||
total = count
|
||||
terminal.UpdateCount(total, !reading, value.(*string))
|
||||
if opts.Sync {
|
||||
opts.Sync = false
|
||||
terminal.UpdateList(PassMerger(&snapshot, opts.Tac), false)
|
||||
}
|
||||
if heightUnknown && !deferred {
|
||||
determine(!reading)
|
||||
}
|
||||
reset := clearCache()
|
||||
matcher.Reset(snapshot, input(reset), false, !reading, sort, reset)
|
||||
|
||||
@@ -295,8 +318,7 @@ func Run(opts *Options, version string, revision string) {
|
||||
if deferred {
|
||||
count := val.Length()
|
||||
if opts.Select1 && count > 1 || opts.Exit0 && !opts.Select1 && count > 0 {
|
||||
deferred = false
|
||||
terminal.startChan <- true
|
||||
determine(val.final)
|
||||
} else if val.final {
|
||||
if opts.Exit0 && count == 0 || opts.Select1 && count == 1 {
|
||||
if opts.PrintQuery {
|
||||
@@ -313,8 +335,7 @@ func Run(opts *Options, version string, revision string) {
|
||||
}
|
||||
os.Exit(exitNoMatch)
|
||||
}
|
||||
deferred = false
|
||||
terminal.startChan <- true
|
||||
determine(val.final)
|
||||
}
|
||||
}
|
||||
terminal.UpdateList(val, clearSelection())
|
||||
|
@@ -53,8 +53,10 @@ const usage = `usage: fzf [options]
|
||||
--jump-labels=CHARS Label characters for jump and jump-accept
|
||||
|
||||
Layout
|
||||
--height=HEIGHT[%] Display fzf window below the cursor with the given
|
||||
height instead of using fullscreen
|
||||
--height=[~]HEIGHT[%] Display fzf window below the cursor with the given
|
||||
height instead of using fullscreen.
|
||||
If prefixed with '~', fzf will determine the height
|
||||
according to the input size.
|
||||
--min-height=HEIGHT Minimum height when --height is given in percent
|
||||
(default: 10)
|
||||
--layout=LAYOUT Choose layout: [default|reverse|reverse-list]
|
||||
@@ -131,6 +133,12 @@ const (
|
||||
byEnd
|
||||
)
|
||||
|
||||
type heightSpec struct {
|
||||
size float64
|
||||
percent bool
|
||||
auto bool
|
||||
}
|
||||
|
||||
type sizeSpec struct {
|
||||
size float64
|
||||
percent bool
|
||||
@@ -180,6 +188,10 @@ type previewOpts struct {
|
||||
alternative *previewOpts
|
||||
}
|
||||
|
||||
func (a previewOpts) aboveOrBelow() bool {
|
||||
return a.size.size > 0 && (a.position == posUp || a.position == posDown)
|
||||
}
|
||||
|
||||
func (a previewOpts) sameLayout(b previewOpts) bool {
|
||||
return a.size == b.size && a.position == b.position && a.border == b.border && a.hidden == b.hidden && a.threshold == b.threshold &&
|
||||
(a.alternative != nil && b.alternative != nil && a.alternative.sameLayout(*b.alternative) ||
|
||||
@@ -211,7 +223,7 @@ type Options struct {
|
||||
Theme *tui.ColorTheme
|
||||
Black bool
|
||||
Bold bool
|
||||
Height sizeSpec
|
||||
Height heightSpec
|
||||
MinHeight int
|
||||
Layout layoutType
|
||||
Cycle bool
|
||||
@@ -1076,11 +1088,6 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) {
|
||||
}
|
||||
if t == actUnbind || t == actRebind {
|
||||
parseKeyChords(actionArg, spec[0:offset]+" target required")
|
||||
} else if t == actChangePreviewWindow {
|
||||
opts := previewOpts{}
|
||||
for _, arg := range strings.Split(actionArg, "|") {
|
||||
parsePreviewWindow(&opts, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1160,9 +1167,17 @@ func parseSize(str string, maxPercent float64, label string) sizeSpec {
|
||||
return sizeSpec{val, percent}
|
||||
}
|
||||
|
||||
func parseHeight(str string) sizeSpec {
|
||||
func parseHeight(str string) heightSpec {
|
||||
heightSpec := heightSpec{}
|
||||
if strings.HasPrefix(str, "~") {
|
||||
heightSpec.auto = true
|
||||
str = str[1:]
|
||||
}
|
||||
|
||||
size := parseSize(str, 100, "height")
|
||||
return size
|
||||
heightSpec.size = size.size
|
||||
heightSpec.percent = size.percent
|
||||
return heightSpec
|
||||
}
|
||||
|
||||
func parseLayout(str string) layoutType {
|
||||
@@ -1525,11 +1540,11 @@ func parseOptions(opts *Options, allArgs []string) {
|
||||
parsePreviewWindow(&opts.Preview,
|
||||
nextString(allArgs, &i, "preview window layout required: [up|down|left|right][,SIZE[%]][,border-BORDER_OPT][,wrap][,cycle][,hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default]"))
|
||||
case "--height":
|
||||
opts.Height = parseHeight(nextString(allArgs, &i, "height required: HEIGHT[%]"))
|
||||
opts.Height = parseHeight(nextString(allArgs, &i, "height required: [~]HEIGHT[%]"))
|
||||
case "--min-height":
|
||||
opts.MinHeight = nextInt(allArgs, &i, "height required: HEIGHT")
|
||||
case "--no-height":
|
||||
opts.Height = sizeSpec{}
|
||||
opts.Height = heightSpec{}
|
||||
case "--no-margin":
|
||||
opts.Margin = defaultMargin()
|
||||
case "--no-padding":
|
||||
@@ -1709,6 +1724,7 @@ func postProcessOptions(opts *Options) {
|
||||
}
|
||||
|
||||
// Extend the default key map
|
||||
previewEnabled := len(opts.Preview.command) > 0 || hasPreviewAction(opts)
|
||||
keymap := defaultKeymap()
|
||||
for key, actions := range opts.Keymap {
|
||||
var lastChangePreviewWindow *action
|
||||
@@ -1719,8 +1735,18 @@ func postProcessOptions(opts *Options) {
|
||||
opts.ToggleSort = true
|
||||
case actChangePreviewWindow:
|
||||
lastChangePreviewWindow = act
|
||||
if !previewEnabled {
|
||||
// Doesn't matter
|
||||
continue
|
||||
}
|
||||
opts := previewOpts{}
|
||||
for _, arg := range strings.Split(act.a, "|") {
|
||||
// Make sure that each expression is valid
|
||||
parsePreviewWindow(&opts, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-organize actions so that we only keep the last change-preview-window
|
||||
// and it comes first in the list.
|
||||
// * change-preview-window(up,+10)+preview(sleep 3; cat {})+change-preview-window(up,+20)
|
||||
@@ -1738,6 +1764,19 @@ func postProcessOptions(opts *Options) {
|
||||
}
|
||||
opts.Keymap = keymap
|
||||
|
||||
if opts.Height.auto {
|
||||
for _, s := range []sizeSpec{opts.Margin[0], opts.Margin[2]} {
|
||||
if s.percent {
|
||||
errorExit("adaptive height is not compatible with top/bottom percent margin")
|
||||
}
|
||||
}
|
||||
for _, s := range []sizeSpec{opts.Padding[0], opts.Padding[2]} {
|
||||
if s.percent {
|
||||
errorExit("adaptive height is not compatible with top/bottom percent padding")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we're not using extended search mode, --nth option becomes irrelevant
|
||||
// if it contains the whole range
|
||||
if !opts.Extended || len(opts.Nth) == 1 {
|
||||
|
151
src/terminal.go
151
src/terminal.go
@@ -100,6 +100,11 @@ type itemLine struct {
|
||||
result Result
|
||||
}
|
||||
|
||||
type fitpad struct {
|
||||
fit int
|
||||
pad int
|
||||
}
|
||||
|
||||
var emptyLine = itemLine{}
|
||||
|
||||
// Terminal represents terminal input/output
|
||||
@@ -183,7 +188,7 @@ type Terminal struct {
|
||||
prevLines []itemLine
|
||||
suppress bool
|
||||
sigstop bool
|
||||
startChan chan bool
|
||||
startChan chan fitpad
|
||||
killChan chan int
|
||||
slab *util.Slab
|
||||
theme *tui.ColorTheme
|
||||
@@ -439,6 +444,13 @@ func makeSpinner(unicode bool) []string {
|
||||
return []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
|
||||
}
|
||||
|
||||
func evaluateHeight(opts *Options, termHeight int) int {
|
||||
if opts.Height.percent {
|
||||
return util.Max(int(opts.Height.size*float64(termHeight)/100.0), opts.MinHeight)
|
||||
}
|
||||
return int(opts.Height.size)
|
||||
}
|
||||
|
||||
// NewTerminal returns new Terminal object
|
||||
func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
input := trimQuery(opts.Query)
|
||||
@@ -465,7 +477,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
strongAttr = tui.AttrRegular
|
||||
}
|
||||
var renderer tui.Renderer
|
||||
fullscreen := opts.Height.size == 0 || opts.Height.percent && opts.Height.size == 100
|
||||
fullscreen := !opts.Height.auto && (opts.Height.size == 0 || opts.Height.percent && opts.Height.size == 100)
|
||||
if fullscreen {
|
||||
if tui.HasFullscreenRenderer() {
|
||||
renderer = tui.NewFullscreenRenderer(opts.Theme, opts.Black, opts.Mouse)
|
||||
@@ -475,24 +487,16 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
}
|
||||
} else {
|
||||
maxHeightFunc := func(termHeight int) int {
|
||||
var maxHeight int
|
||||
if opts.Height.percent {
|
||||
maxHeight = util.Max(int(opts.Height.size*float64(termHeight)/100.0), opts.MinHeight)
|
||||
} else {
|
||||
maxHeight = int(opts.Height.size)
|
||||
}
|
||||
|
||||
// Minimum height required to render fzf excluding margin and padding
|
||||
effectiveMinHeight := minHeight
|
||||
if previewBox != nil && (opts.Preview.position == posUp || opts.Preview.position == posDown) {
|
||||
effectiveMinHeight *= 2
|
||||
if previewBox != nil && opts.Preview.aboveOrBelow() {
|
||||
effectiveMinHeight += 1 + borderLines(opts.Preview.border)
|
||||
}
|
||||
if opts.InfoStyle != infoDefault {
|
||||
effectiveMinHeight--
|
||||
}
|
||||
if opts.BorderShape != tui.BorderNone {
|
||||
effectiveMinHeight += 2
|
||||
}
|
||||
return util.Min(termHeight, util.Max(maxHeight, effectiveMinHeight))
|
||||
effectiveMinHeight += borderLines(opts.BorderShape)
|
||||
return util.Min(termHeight, util.Max(evaluateHeight(opts, termHeight), effectiveMinHeight))
|
||||
}
|
||||
renderer = tui.NewLightRenderer(opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, opts.ClearOnExit, false, maxHeightFunc)
|
||||
}
|
||||
@@ -572,7 +576,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
sigstop: false,
|
||||
slab: util.MakeSlab(slab16Size, slab32Size),
|
||||
theme: opts.Theme,
|
||||
startChan: make(chan bool, 1),
|
||||
startChan: make(chan fitpad, 1),
|
||||
killChan: make(chan int),
|
||||
tui: renderer,
|
||||
initFunc: func() { renderer.Init() },
|
||||
@@ -587,6 +591,32 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
return &t
|
||||
}
|
||||
|
||||
func borderLines(shape tui.BorderShape) int {
|
||||
switch shape {
|
||||
case tui.BorderHorizontal, tui.BorderRounded, tui.BorderSharp:
|
||||
return 2
|
||||
case tui.BorderTop, tui.BorderBottom:
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Extra number of lines needed to display fzf
|
||||
func (t *Terminal) extraLines() int {
|
||||
extra := len(t.header0) + t.headerLines + 1
|
||||
if !t.noInfoLine() {
|
||||
extra++
|
||||
}
|
||||
return extra
|
||||
}
|
||||
|
||||
func (t *Terminal) MaxFitAndPad(opts *Options) (int, int) {
|
||||
_, screenHeight, marginInt, paddingInt := t.adjustMarginAndPadding()
|
||||
padHeight := marginInt[0] + marginInt[2] + paddingInt[0] + paddingInt[2]
|
||||
fit := screenHeight - padHeight - t.extraLines()
|
||||
return fit, padHeight
|
||||
}
|
||||
|
||||
func (t *Terminal) parsePrompt(prompt string) (func(), int) {
|
||||
var state *ansiState
|
||||
trimmed, colors, _ := extractColor(prompt, state, nil)
|
||||
@@ -725,22 +755,23 @@ func (t *Terminal) displayWidth(runes []rune) int {
|
||||
|
||||
const (
|
||||
minWidth = 4
|
||||
minHeight = 4
|
||||
minHeight = 3
|
||||
)
|
||||
|
||||
func calculateSize(base int, size sizeSpec, occupied int, minSize int, pad int) int {
|
||||
max := base - occupied
|
||||
if max < minSize {
|
||||
max = minSize
|
||||
}
|
||||
if size.percent {
|
||||
return util.Constrain(int(float64(base)*0.01*size.size), minSize, max)
|
||||
}
|
||||
return util.Constrain(int(size.size)+pad, minSize, max)
|
||||
}
|
||||
|
||||
func (t *Terminal) resizeWindows() {
|
||||
func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
|
||||
screenWidth := t.tui.MaxX()
|
||||
screenHeight := t.tui.MaxY()
|
||||
t.prevLines = make([]itemLine, screenHeight)
|
||||
|
||||
marginInt := [4]int{} // TRBL
|
||||
paddingInt := [4]int{} // TRBL
|
||||
sizeSpecToInt := func(index int, spec sizeSpec) int {
|
||||
@@ -789,31 +820,48 @@ func (t *Terminal) resizeWindows() {
|
||||
}
|
||||
|
||||
adjust := func(idx1 int, idx2 int, max int, min int) {
|
||||
if max >= min {
|
||||
margin := marginInt[idx1] + marginInt[idx2] + paddingInt[idx1] + paddingInt[idx2]
|
||||
if max-margin < min {
|
||||
desired := max - min
|
||||
paddingInt[idx1] = desired * paddingInt[idx1] / margin
|
||||
paddingInt[idx2] = desired * paddingInt[idx2] / margin
|
||||
marginInt[idx1] = util.Max(extraMargin[idx1], desired*marginInt[idx1]/margin)
|
||||
marginInt[idx2] = util.Max(extraMargin[idx2], desired*marginInt[idx2]/margin)
|
||||
}
|
||||
if min > max {
|
||||
min = max
|
||||
}
|
||||
margin := marginInt[idx1] + marginInt[idx2] + paddingInt[idx1] + paddingInt[idx2]
|
||||
if max-margin < min {
|
||||
desired := max - min
|
||||
paddingInt[idx1] = desired * paddingInt[idx1] / margin
|
||||
paddingInt[idx2] = desired * paddingInt[idx2] / margin
|
||||
marginInt[idx1] = util.Max(extraMargin[idx1], desired*marginInt[idx1]/margin)
|
||||
marginInt[idx2] = util.Max(extraMargin[idx2], desired*marginInt[idx2]/margin)
|
||||
}
|
||||
}
|
||||
|
||||
previewVisible := t.isPreviewEnabled() && t.previewOpts.size.size > 0
|
||||
minAreaWidth := minWidth
|
||||
minAreaHeight := minHeight
|
||||
if previewVisible {
|
||||
if t.noInfoLine() {
|
||||
minAreaHeight -= 1
|
||||
}
|
||||
if t.isPreviewVisible() {
|
||||
minPreviewHeight := 1 + borderLines(t.previewOpts.border)
|
||||
minPreviewWidth := 5
|
||||
switch t.previewOpts.position {
|
||||
case posUp, posDown:
|
||||
minAreaHeight *= 2
|
||||
minAreaHeight += minPreviewHeight
|
||||
minAreaWidth = util.Max(minPreviewWidth, minAreaWidth)
|
||||
case posLeft, posRight:
|
||||
minAreaWidth *= 2
|
||||
minAreaWidth += minPreviewWidth
|
||||
minAreaHeight = util.Max(minPreviewHeight, minAreaHeight)
|
||||
}
|
||||
}
|
||||
adjust(1, 3, screenWidth, minAreaWidth)
|
||||
adjust(0, 2, screenHeight, minAreaHeight)
|
||||
|
||||
return screenWidth, screenHeight, marginInt, paddingInt
|
||||
}
|
||||
|
||||
func (t *Terminal) resizeWindows() {
|
||||
screenWidth, screenHeight, marginInt, paddingInt := t.adjustMarginAndPadding()
|
||||
width := screenWidth - marginInt[1] - marginInt[3]
|
||||
height := screenHeight - marginInt[0] - marginInt[2]
|
||||
|
||||
t.prevLines = make([]itemLine, screenHeight)
|
||||
if t.border != nil {
|
||||
t.border.Close()
|
||||
}
|
||||
@@ -832,8 +880,6 @@ func (t *Terminal) resizeWindows() {
|
||||
// Reset preview version so that full redraw occurs
|
||||
t.previewed.version = 0
|
||||
|
||||
width := screenWidth - marginInt[1] - marginInt[3]
|
||||
height := screenHeight - marginInt[0] - marginInt[2]
|
||||
switch t.borderShape {
|
||||
case tui.BorderHorizontal:
|
||||
t.border = t.tui.NewWindow(
|
||||
@@ -865,16 +911,16 @@ func (t *Terminal) resizeWindows() {
|
||||
false, tui.MakeBorderStyle(t.borderShape, t.unicode))
|
||||
}
|
||||
|
||||
// Add padding
|
||||
// Add padding to margin
|
||||
for idx, val := range paddingInt {
|
||||
marginInt[idx] += val
|
||||
}
|
||||
width = screenWidth - marginInt[1] - marginInt[3]
|
||||
height = screenHeight - marginInt[0] - marginInt[2]
|
||||
width -= paddingInt[1] + paddingInt[3]
|
||||
height -= paddingInt[0] + paddingInt[2]
|
||||
|
||||
// Set up preview window
|
||||
noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode)
|
||||
if previewVisible {
|
||||
if t.isPreviewVisible() {
|
||||
var resizePreviewWindows func(previewOpts previewOpts)
|
||||
resizePreviewWindows = func(previewOpts previewOpts) {
|
||||
hasThreshold := previewOpts.threshold > 0 && previewOpts.alternative != nil
|
||||
@@ -1863,6 +1909,10 @@ func (t *Terminal) isPreviewEnabled() bool {
|
||||
return t.hasPreviewer() && t.previewer.enabled
|
||||
}
|
||||
|
||||
func (t *Terminal) isPreviewVisible() bool {
|
||||
return t.isPreviewEnabled() && t.previewOpts.size.size > 0
|
||||
}
|
||||
|
||||
func (t *Terminal) hasPreviewWindow() bool {
|
||||
return t.pwindow != nil && t.isPreviewEnabled()
|
||||
}
|
||||
@@ -1962,7 +2012,28 @@ func (t *Terminal) cancelPreview() {
|
||||
// Loop is called to start Terminal I/O
|
||||
func (t *Terminal) Loop() {
|
||||
// prof := profile.Start(profile.ProfilePath("/tmp/"))
|
||||
<-t.startChan
|
||||
fitpad := <-t.startChan
|
||||
fit := fitpad.fit
|
||||
if fit >= 0 {
|
||||
pad := fitpad.pad
|
||||
t.tui.Resize(func(termHeight int) int {
|
||||
contentHeight := fit + t.extraLines()
|
||||
if t.hasPreviewer() {
|
||||
if t.previewOpts.aboveOrBelow() {
|
||||
if t.previewOpts.size.percent {
|
||||
newContentHeight := int(float64(contentHeight) * 100. / (100. - t.previewOpts.size.size))
|
||||
contentHeight = util.Max(contentHeight+1+borderLines(t.previewOpts.border), newContentHeight)
|
||||
} else {
|
||||
contentHeight += int(t.previewOpts.size.size) + borderLines(t.previewOpts.border)
|
||||
}
|
||||
} else {
|
||||
// Minimum height if preview window can appear
|
||||
contentHeight = util.Max(contentHeight, 1+borderLines(t.previewOpts.border))
|
||||
}
|
||||
}
|
||||
return util.Min(termHeight, contentHeight+pad)
|
||||
})
|
||||
}
|
||||
{ // Late initialization
|
||||
intChan := make(chan os.Signal, 1)
|
||||
signal.Notify(intChan, os.Interrupt, syscall.SIGTERM)
|
||||
|
@@ -27,12 +27,13 @@ const (
|
||||
StrikeThrough = Attr(1 << 7)
|
||||
)
|
||||
|
||||
func (r *FullscreenRenderer) Init() {}
|
||||
func (r *FullscreenRenderer) Pause(bool) {}
|
||||
func (r *FullscreenRenderer) Resume(bool, bool) {}
|
||||
func (r *FullscreenRenderer) Clear() {}
|
||||
func (r *FullscreenRenderer) Refresh() {}
|
||||
func (r *FullscreenRenderer) Close() {}
|
||||
func (r *FullscreenRenderer) Init() {}
|
||||
func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
|
||||
func (r *FullscreenRenderer) Pause(bool) {}
|
||||
func (r *FullscreenRenderer) Resume(bool, bool) {}
|
||||
func (r *FullscreenRenderer) Clear() {}
|
||||
func (r *FullscreenRenderer) Refresh() {}
|
||||
func (r *FullscreenRenderer) Close() {}
|
||||
|
||||
func (r *FullscreenRenderer) GetChar() Event { return Event{} }
|
||||
func (r *FullscreenRenderer) MaxX() int { return 0 }
|
||||
|
@@ -189,6 +189,10 @@ func (r *LightRenderer) Init() {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LightRenderer) Resize(maxHeightFunc func(int) int) {
|
||||
r.maxHeightFunc = maxHeightFunc
|
||||
}
|
||||
|
||||
func (r *LightRenderer) makeSpace() {
|
||||
r.stderr("\n")
|
||||
r.csi("G")
|
||||
@@ -676,6 +680,9 @@ func (r *LightRenderer) MaxX() int {
|
||||
}
|
||||
|
||||
func (r *LightRenderer) MaxY() int {
|
||||
if r.height == 0 {
|
||||
r.updateTerminalSize()
|
||||
}
|
||||
return r.height
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,10 @@ func HasFullscreenRenderer() bool {
|
||||
}
|
||||
|
||||
func asTcellColor(color Color) tcell.Color {
|
||||
if color == colDefault {
|
||||
return tcell.ColorDefault
|
||||
}
|
||||
|
||||
value := uint64(tcell.ColorValid) + uint64(color)
|
||||
if color.is24() {
|
||||
value = value | uint64(tcell.ColorIsRGB)
|
||||
@@ -95,6 +99,8 @@ const (
|
||||
AttrClear = Attr(1 << 8)
|
||||
)
|
||||
|
||||
func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
|
||||
|
||||
func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
|
||||
if _screen.Colors() >= 256 {
|
||||
return Dark256
|
||||
|
@@ -358,6 +358,7 @@ func MakeTransparentBorder() BorderStyle {
|
||||
|
||||
type Renderer interface {
|
||||
Init()
|
||||
Resize(maxHeightFunc func(int) int)
|
||||
Pause(clear bool)
|
||||
Resume(clear bool, sigcont bool)
|
||||
Clear()
|
||||
|
@@ -2245,6 +2245,93 @@ class TestGoFZF < TestBase
|
||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||
tmux.until { |lines| assert_match(/^> SNIPSNIP.*SNIPSNIP$/, lines[-3]) }
|
||||
end
|
||||
|
||||
def assert_block(expected, lines)
|
||||
cols = expected.lines.map(&:chomp).map(&:length).max
|
||||
actual = lines.reverse.take(expected.lines.length).reverse.map { _1[0, cols].rstrip + "\n" }.join
|
||||
assert_equal expected, actual
|
||||
end
|
||||
|
||||
def test_height_range_fit
|
||||
tmux.send_keys 'seq 3 | fzf --height ~100% --info=inline --border', :Enter
|
||||
expected = <<~OUTPUT
|
||||
╭──────────
|
||||
│ 3
|
||||
│ 2
|
||||
│ > 1
|
||||
│ > < 3/3
|
||||
╰──────────
|
||||
OUTPUT
|
||||
tmux.until { assert_block(expected, _1) }
|
||||
end
|
||||
|
||||
def test_height_range_fit_preview_above
|
||||
tmux.send_keys 'seq 3 | fzf --height ~100% --info=inline --border --preview "seq {}" --preview-window up,60%', :Enter
|
||||
expected = <<~OUTPUT
|
||||
╭──────────
|
||||
│ ╭────────
|
||||
│ │ 1
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╰────────
|
||||
│ 3
|
||||
│ 2
|
||||
│ > 1
|
||||
│ > < 3/3
|
||||
╰──────────
|
||||
OUTPUT
|
||||
tmux.until { assert_block(expected, _1) }
|
||||
end
|
||||
|
||||
def test_height_range_fit_preview_above_alternative
|
||||
tmux.send_keys 'seq 3 | fzf --height ~100% --border=sharp --preview "seq {}" --preview-window up,40%,border-bottom --padding 1 --exit-0 --header hello --header-lines=2', :Enter
|
||||
expected = <<~OUTPUT
|
||||
┌─────────
|
||||
│
|
||||
│ 1
|
||||
│ 2
|
||||
│ 3
|
||||
│ ───────
|
||||
│ > 3
|
||||
│ 2
|
||||
│ 1
|
||||
│ hello
|
||||
│ 1/1
|
||||
│ >
|
||||
│
|
||||
└─────────
|
||||
OUTPUT
|
||||
tmux.until { assert_block(expected, _1) }
|
||||
end
|
||||
|
||||
def test_height_range_fit_preview_left
|
||||
tmux.send_keys "seq 3 | fzf --height ~100% --border=vertical --preview 'seq {}' --preview-window left,5,border-right --padding 1 --exit-0 --header $'hello\\nworld' --header-lines=2", :Enter
|
||||
expected = <<~OUTPUT
|
||||
│
|
||||
│ 1 │> 3
|
||||
│ 2 │ 2
|
||||
│ 3 │ 1
|
||||
│ │ hello
|
||||
│ │ world
|
||||
│ │ 1/1
|
||||
│ │>
|
||||
│
|
||||
OUTPUT
|
||||
tmux.until { assert_block(expected, _1) }
|
||||
end
|
||||
|
||||
def test_height_range_overflow
|
||||
tmux.send_keys 'seq 100 | fzf --height ~5 --info=inline --border', :Enter
|
||||
expected = <<~OUTPUT
|
||||
╭──────────────
|
||||
│ 2
|
||||
│ > 1
|
||||
│ > < 100/100
|
||||
╰──────────────
|
||||
OUTPUT
|
||||
tmux.until { assert_block(expected, _1) }
|
||||
end
|
||||
end
|
||||
|
||||
module TestShell
|
||||
|
Reference in New Issue
Block a user