mirror of
https://github.com/junegunn/fzf.git
synced 2025-08-16 12:43:50 -07:00
Compare commits
153 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fc7630a66d | ||
|
3248153d9f | ||
|
246b9f3130 | ||
|
865144850d | ||
|
d9752a4c21 | ||
|
dba14d2630 | ||
|
2986e64a49 | ||
|
1d8bd11b67 | ||
|
bafb99d520 | ||
|
3cc8a74a91 | ||
|
c0aa5a438f | ||
|
825d401403 | ||
|
9dfca77c36 | ||
|
82c4af2902 | ||
|
736344e151 | ||
|
6f9663da62 | ||
|
f8ae1786dd | ||
|
c60ed17583 | ||
|
e0f0b5bcf9 | ||
|
9e96073128 | ||
|
0db65c22d3 | ||
|
d785135606 | ||
|
ae15eda546 | ||
|
f2d44ab5a7 | ||
|
43798fc2e8 | ||
|
9dc4b40d7a | ||
|
1cb19dbf65 | ||
|
1ab4289ad6 | ||
|
e2ae1b249c | ||
|
92b7efafca | ||
|
f092e4038f | ||
|
aa5dae391b | ||
|
08a6fd4ad4 | ||
|
a61150a96c | ||
|
0f9cb5590e | ||
|
c0a83b27eb | ||
|
f79b1f71b8 | ||
|
8e027c445f | ||
|
dda3e3c39a | ||
|
fd5157998c | ||
|
e0217e8c79 | ||
|
3ab1c42266 | ||
|
d1676776aa | ||
|
bdde69d011 | ||
|
6dec42a33a | ||
|
199bc3f0ad | ||
|
17dd833925 | ||
|
4ec144c969 | ||
|
3e36f2b0ac | ||
|
07a03b3e73 | ||
|
c33258832e | ||
|
a7aa08ce07 | ||
|
06d63a862e | ||
|
43d1c4c4b5 | ||
|
f81feb1e69 | ||
|
01cf01e084 | ||
|
97a725fbd0 | ||
|
ace92ba281 | ||
|
d631c76e8d | ||
|
e6d33f77da | ||
|
a6d3e3687b | ||
|
08c2bcb952 | ||
|
98ca4bdede | ||
|
3f8e741562 | ||
|
6e464ebd9b | ||
|
c329279339 | ||
|
cf04753ad7 | ||
|
69e7eab11f | ||
|
dea206b023 | ||
|
5deaf58928 | ||
|
15e2952a2b | ||
|
a9fba1c849 | ||
|
71e573d082 | ||
|
334a4fa159 | ||
|
21f94ee800 | ||
|
540bfd7a72 | ||
|
8fbed2b13a | ||
|
aa17510e0a | ||
|
bf65e8cd12 | ||
|
0f5c6e8f04 | ||
|
b1b916ce15 | ||
|
a6a732e1fc | ||
|
a5c2f28539 | ||
|
18261fe31c | ||
|
079046863c | ||
|
07b965bba1 | ||
|
c39113ee41 | ||
|
14f90502a4 | ||
|
b0673c3563 | ||
|
373c6d8d55 | ||
|
b8fc828955 | ||
|
b43b040512 | ||
|
50b7608f9d | ||
|
7085e5b629 | ||
|
7d5985baf9 | ||
|
7c40a424c0 | ||
|
baf882ace7 | ||
|
ba82f0bef9 | ||
|
d9c6a0305b | ||
|
d9b1211191 | ||
|
99f1e02766 | ||
|
242c0db26b | ||
|
dd49e41c42 | ||
|
6db15e8693 | ||
|
4c9cab3f8a | ||
|
b2c0413a98 | ||
|
e34c7c00b1 | ||
|
7c447bbdc7 | ||
|
7bf1f2cc84 | ||
|
afa2c4e0af | ||
|
2ff7db1b36 | ||
|
9f0626da64 | ||
|
d8cb5c1cf5 | ||
|
dca56da0ef | ||
|
ec75d16ea8 | ||
|
5cae8ea733 | ||
|
1ccd8f6a64 | ||
|
9c293bb82b | ||
|
9897ee9591 | ||
|
5215415315 | ||
|
54891d11e0 | ||
|
567c8303bf | ||
|
2a60edcd52 | ||
|
d61ac32d7b | ||
|
b57e6cff7e | ||
|
5b99f19dac | ||
|
6c03571887 | ||
|
4fb410a93c | ||
|
5e1db9fdd3 | ||
|
9d7480ae3c | ||
|
f39cf6d855 | ||
|
001d116884 | ||
|
02c5e62efe | ||
|
446df07b62 | ||
|
8583b150c9 | ||
|
a859aa72ee | ||
|
0896036266 | ||
|
311b78ae82 | ||
|
f5cf4fc8fb | ||
|
7ceb58b2aa | ||
|
293dd76af1 | ||
|
3918c45ced | ||
|
4ec403347c | ||
|
e01266ffcb | ||
|
f246fb2fc2 | ||
|
f7b26b34cb | ||
|
a1bcdc225e | ||
|
7771241cc0 | ||
|
6e3af646b2 | ||
|
82bf8c138d | ||
|
e21b001116 | ||
|
577024f1e9 | ||
|
d4ad4a25db |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@ doc/tags
|
|||||||
vendor
|
vendor
|
||||||
gopath
|
gopath
|
||||||
*.zwc
|
*.zwc
|
||||||
|
fzf
|
||||||
|
24
.rubocop.yml
Normal file
24
.rubocop.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
Layout/LineLength:
|
||||||
|
Enabled: false
|
||||||
|
Metrics:
|
||||||
|
Enabled: false
|
||||||
|
Lint/ShadowingOuterLocalVariable:
|
||||||
|
Enabled: false
|
||||||
|
Style/MethodCallWithArgsParentheses:
|
||||||
|
Enabled: true
|
||||||
|
IgnoredMethods:
|
||||||
|
- assert
|
||||||
|
- exit
|
||||||
|
- paste
|
||||||
|
- puts
|
||||||
|
- raise
|
||||||
|
- refute
|
||||||
|
- require
|
||||||
|
- send_keys
|
||||||
|
IgnoredPatterns:
|
||||||
|
- ^assert_
|
||||||
|
- ^refute_
|
||||||
|
Style/NumericPredicate:
|
||||||
|
Enabled: false
|
||||||
|
Style/WordArray:
|
||||||
|
MinSize: 1
|
45
.travis.yml
45
.travis.yml
@@ -1,27 +1,28 @@
|
|||||||
language: go
|
language: go
|
||||||
dist: xenial
|
go:
|
||||||
|
- "1.14"
|
||||||
|
env: GO111MODULE=on
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
dist: bionic
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources:
|
|
||||||
- sourceline: "ppa:pi-rho/dev"
|
|
||||||
- sourceline: "ppa:fish-shell/release-2"
|
|
||||||
packages:
|
packages:
|
||||||
- tmux
|
|
||||||
- zsh
|
|
||||||
- fish
|
- fish
|
||||||
|
- zsh
|
||||||
env:
|
sources:
|
||||||
- GO111MODULE=on
|
sourceline: ppa:fish-shell/release-3
|
||||||
|
homebrew:
|
||||||
jobs:
|
packages:
|
||||||
include:
|
- fish
|
||||||
- stage: unittest
|
- tmux
|
||||||
go: "1.13.x"
|
update: true
|
||||||
script: make && make test
|
install: gem install minitest rubocop rubocop-minitest rubocop-performance
|
||||||
|
script:
|
||||||
- stage: cli
|
- make test
|
||||||
go: "1.13.x"
|
# LC_ALL=C to avoid escape codes in
|
||||||
rvm: "2.5"
|
# printf %q $'\355\205\214\354\212\244\355\212\270' on macOS. Bash on
|
||||||
script: |
|
# macOS is built without HANDLE_MULTIBYTE?
|
||||||
make install && ./install --all && tmux new "ruby test/test_go.rb > out && touch ok" && cat out && [ -e ok ]
|
- make install && ./install --all && LC_ALL=C tmux new-session -d && ruby test/test_go.rb --verbose
|
||||||
|
- rubocop --require rubocop-minitest --require rubocop-performance
|
||||||
|
145
CHANGELOG.md
145
CHANGELOG.md
@@ -1,6 +1,151 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
0.23.1
|
||||||
|
------
|
||||||
|
- Added `--preview-window` options for disabling flags
|
||||||
|
- `nocycle`
|
||||||
|
- `nohidden`
|
||||||
|
- `nowrap`
|
||||||
|
- `default`
|
||||||
|
- Built with Go 1.14.9 due to performance regression
|
||||||
|
- https://github.com/golang/go/issues/40727
|
||||||
|
|
||||||
|
0.23.0
|
||||||
|
------
|
||||||
|
- Support preview scroll offset relative to window height
|
||||||
|
```sh
|
||||||
|
git grep --line-number '' |
|
||||||
|
fzf --delimiter : \
|
||||||
|
--preview 'bat --style=numbers --color=always --highlight-line {2} {1}' \
|
||||||
|
--preview-window +{2}-/2
|
||||||
|
```
|
||||||
|
- Added `--preview-window` option for sharp edges (`--preview-window sharp`)
|
||||||
|
- Added `--preview-window` option for cyclic scrolling (`--preview-window cycle`)
|
||||||
|
- Reduced vertical padding around the preview window when `--preview-window
|
||||||
|
noborder` is used
|
||||||
|
- Added actions for preview window
|
||||||
|
- `preview-half-page-up`
|
||||||
|
- `preview-half-page-down`
|
||||||
|
- Vim
|
||||||
|
- Popup width and height can be given in absolute integer values
|
||||||
|
- Added `fzf#exec()` function for getting the path of fzf executable
|
||||||
|
- It also downloads the latest binary if it's not available by running
|
||||||
|
`./install --bin`
|
||||||
|
- Built with Go 1.15.2
|
||||||
|
- We no longer provide 32-bit binaries
|
||||||
|
|
||||||
|
0.22.0
|
||||||
|
------
|
||||||
|
- Added more options for `--bind`
|
||||||
|
- `backward-eof` event
|
||||||
|
```sh
|
||||||
|
# Aborts when you delete backward when the query prompt is already empty
|
||||||
|
fzf --bind backward-eof:abort
|
||||||
|
```
|
||||||
|
- `refresh-preview` action
|
||||||
|
```sh
|
||||||
|
# Rerun preview command when you hit '?'
|
||||||
|
fzf --preview 'echo $RANDOM' --bind '?:refresh-preview'
|
||||||
|
```
|
||||||
|
- `preview` action
|
||||||
|
```sh
|
||||||
|
# Default preview command with an extra preview binding
|
||||||
|
fzf --preview 'file {}' --bind '?:preview:cat {}'
|
||||||
|
|
||||||
|
# A preview binding with no default preview command
|
||||||
|
# (Preview window is initially empty)
|
||||||
|
fzf --bind '?:preview:cat {}'
|
||||||
|
|
||||||
|
# Preview window hidden by default, it appears when you first hit '?'
|
||||||
|
fzf --bind '?:preview:cat {}' --preview-window hidden
|
||||||
|
```
|
||||||
|
- Added preview window option for setting the initial scroll offset
|
||||||
|
```sh
|
||||||
|
# Initial scroll offset is set to the line number of each line of
|
||||||
|
# git grep output *minus* 5 lines
|
||||||
|
git grep --line-number '' |
|
||||||
|
fzf --delimiter : --preview 'nl {1}' --preview-window +{2}-5
|
||||||
|
```
|
||||||
|
- Added support for ANSI colors in `--prompt` string
|
||||||
|
- Smart match of accented characters
|
||||||
|
- An unaccented character in the query string will match both accented and
|
||||||
|
unaccented characters, while an accented character will only match
|
||||||
|
accented characters. This is similar to how "smart-case" match works.
|
||||||
|
- Vim plugin
|
||||||
|
- `tmux` layout option for using fzf-tmux
|
||||||
|
```vim
|
||||||
|
let g:fzf_layout = { 'tmux': '-p90%,60%' }
|
||||||
|
```
|
||||||
|
|
||||||
|
0.21.1
|
||||||
|
------
|
||||||
|
- Shell extension
|
||||||
|
- CTRL-R will remove duplicate commands
|
||||||
|
- fzf-tmux
|
||||||
|
- Supports tmux popup window (require tmux 3.2 or above)
|
||||||
|
- ```sh
|
||||||
|
# 50% width and height
|
||||||
|
fzf-tmux -p
|
||||||
|
|
||||||
|
# 80% width and height
|
||||||
|
fzf-tmux -p 80%
|
||||||
|
|
||||||
|
# 80% width and 40% height
|
||||||
|
fzf-tmux -p 80%,40%
|
||||||
|
fzf-tmux -w 80% -h 40%
|
||||||
|
|
||||||
|
# Window position
|
||||||
|
fzf-tmux -w 80% -h 40% -x 0 -y 0
|
||||||
|
fzf-tmux -w 80% -h 40% -y 1000
|
||||||
|
|
||||||
|
# Write ordinary fzf options after --
|
||||||
|
fzf-tmux -p -- --reverse --info=inline --margin 2,4 --border
|
||||||
|
```
|
||||||
|
- On macOS, you can build the latest tmux from the source with
|
||||||
|
`brew install tmux --HEAD`
|
||||||
|
- Bug fixes
|
||||||
|
- Fixed Windows file traversal not to include directories
|
||||||
|
- Fixed ANSI colors with `--keep-right`
|
||||||
|
- Fixed _fzf_complete for zsh
|
||||||
|
- Built with Go 1.14.1
|
||||||
|
|
||||||
|
0.21.0
|
||||||
|
------
|
||||||
|
- `--height` option is now available on Windows as well (@kelleyma49)
|
||||||
|
- Added `--pointer` and `--marker` options
|
||||||
|
- Added `--keep-right` option that keeps the right end of the line visible
|
||||||
|
when it's too long
|
||||||
|
- Style changes
|
||||||
|
- `--border` will now print border with rounded corners around the
|
||||||
|
finder instead of printing horizontal lines above and below it.
|
||||||
|
The previous style is available via `--border=horizontal`
|
||||||
|
- Unicode spinner
|
||||||
|
- More keys and actions for `--bind`
|
||||||
|
- Added PowerShell script for downloading Windows binary
|
||||||
|
- Vim plugin: Built-in floating windows support
|
||||||
|
```vim
|
||||||
|
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||||
|
```
|
||||||
|
- bash: Various improvements in key bindings (CTRL-T, CTRL-R, ALT-C)
|
||||||
|
- CTRL-R will start with the current command-line as the initial query
|
||||||
|
- CTRL-R properly supports multi-line commands
|
||||||
|
- Fuzzy completion API changed
|
||||||
|
```sh
|
||||||
|
# Previous: fzf arguments given as a single string argument
|
||||||
|
# - This style is still supported, but it's deprecated
|
||||||
|
_fzf_complete "--multi --reverse --prompt=\"doge> \"" "$@" < <(
|
||||||
|
echo foo
|
||||||
|
)
|
||||||
|
|
||||||
|
# New API: multiple fzf arguments before "--"
|
||||||
|
# - Easier to write multiple options
|
||||||
|
_fzf_complete --multi --reverse --prompt="doge> " -- "$@" < <(
|
||||||
|
echo foo
|
||||||
|
)
|
||||||
|
```
|
||||||
|
- Bug fixes and improvements
|
||||||
|
|
||||||
0.20.0
|
0.20.0
|
||||||
------
|
------
|
||||||
- Customizable preview window color (`preview-fg` and `preview-bg` for `--color`)
|
- Customizable preview window color (`preview-fg` and `preview-bg` for `--color`)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
FROM archlinux/base:latest
|
FROM archlinux/base:latest
|
||||||
RUN pacman -Sy && pacman --noconfirm -S awk git tmux zsh fish ruby procps go make
|
RUN pacman -Sy && pacman --noconfirm -S awk git tmux zsh fish ruby procps go make gcc
|
||||||
RUN gem install --no-document minitest
|
RUN gem install --no-document minitest
|
||||||
RUN echo '. /usr/share/bash-completion/completions/git' >> ~/.bashrc
|
RUN echo '. /usr/share/bash-completion/completions/git' >> ~/.bashrc
|
||||||
RUN echo '. ~/.bashrc' >> ~/.bash_profile
|
RUN echo '. ~/.bashrc' >> ~/.bash_profile
|
||||||
|
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2016 Junegunn Choi
|
Copyright (c) 2013-2020 Junegunn Choi
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
20
Makefile
20
Makefile
@@ -6,9 +6,8 @@ ROOT_DIR := $(shell dirname $(MAKEFILE))
|
|||||||
SOURCES := $(wildcard *.go src/*.go src/*/*.go) $(MAKEFILE)
|
SOURCES := $(wildcard *.go src/*.go src/*/*.go) $(MAKEFILE)
|
||||||
|
|
||||||
REVISION := $(shell git log -n 1 --pretty=format:%h -- $(SOURCES))
|
REVISION := $(shell git log -n 1 --pretty=format:%h -- $(SOURCES))
|
||||||
BUILD_FLAGS := -a -ldflags "-X main.revision=$(REVISION) -w -extldflags=$(LDFLAGS)" -tags "$(TAGS)"
|
BUILD_FLAGS := -a -ldflags "-X main.revision=$(REVISION) -w '-extldflags=$(LDFLAGS)'" -tags "$(TAGS)"
|
||||||
|
|
||||||
BINARY32 := fzf-$(GOOS)_386
|
|
||||||
BINARY64 := fzf-$(GOOS)_amd64
|
BINARY64 := fzf-$(GOOS)_amd64
|
||||||
BINARYARM5 := fzf-$(GOOS)_arm5
|
BINARYARM5 := fzf-$(GOOS)_arm5
|
||||||
BINARYARM6 := fzf-$(GOOS)_arm6
|
BINARYARM6 := fzf-$(GOOS)_arm6
|
||||||
@@ -16,7 +15,6 @@ BINARYARM7 := fzf-$(GOOS)_arm7
|
|||||||
BINARYARM8 := fzf-$(GOOS)_arm8
|
BINARYARM8 := fzf-$(GOOS)_arm8
|
||||||
BINARYPPC64LE := fzf-$(GOOS)_ppc64le
|
BINARYPPC64LE := fzf-$(GOOS)_ppc64le
|
||||||
VERSION := $(shell awk -F= '/version =/ {print $$2}' src/constants.go | tr -d "\" ")
|
VERSION := $(shell awk -F= '/version =/ {print $$2}' src/constants.go | tr -d "\" ")
|
||||||
RELEASE32 := fzf-$(VERSION)-$(GOOS)_386
|
|
||||||
RELEASE64 := fzf-$(VERSION)-$(GOOS)_amd64
|
RELEASE64 := fzf-$(VERSION)-$(GOOS)_amd64
|
||||||
RELEASEARM5 := fzf-$(VERSION)-$(GOOS)_arm5
|
RELEASEARM5 := fzf-$(VERSION)-$(GOOS)_arm5
|
||||||
RELEASEARM6 := fzf-$(VERSION)-$(GOOS)_arm6
|
RELEASEARM6 := fzf-$(VERSION)-$(GOOS)_arm6
|
||||||
@@ -30,10 +28,6 @@ ifeq ($(UNAME_M),x86_64)
|
|||||||
BINARY := $(BINARY64)
|
BINARY := $(BINARY64)
|
||||||
else ifeq ($(UNAME_M),amd64)
|
else ifeq ($(UNAME_M),amd64)
|
||||||
BINARY := $(BINARY64)
|
BINARY := $(BINARY64)
|
||||||
else ifeq ($(UNAME_M),i686)
|
|
||||||
BINARY := $(BINARY32)
|
|
||||||
else ifeq ($(UNAME_M),i386)
|
|
||||||
BINARY := $(BINARY32)
|
|
||||||
else ifeq ($(UNAME_M),armv5l)
|
else ifeq ($(UNAME_M),armv5l)
|
||||||
BINARY := $(BINARYARM5)
|
BINARY := $(BINARYARM5)
|
||||||
else ifeq ($(UNAME_M),armv6l)
|
else ifeq ($(UNAME_M),armv6l)
|
||||||
@@ -56,13 +50,11 @@ target:
|
|||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
||||||
ifeq ($(GOOS),windows)
|
ifeq ($(GOOS),windows)
|
||||||
release: target/$(BINARY32) target/$(BINARY64)
|
release: target/$(BINARY64)
|
||||||
cd target && cp -f $(BINARY32) fzf.exe && zip $(RELEASE32).zip fzf.exe
|
|
||||||
cd target && cp -f $(BINARY64) fzf.exe && zip $(RELEASE64).zip fzf.exe
|
cd target && cp -f $(BINARY64) fzf.exe && zip $(RELEASE64).zip fzf.exe
|
||||||
cd target && rm -f fzf.exe
|
cd target && rm -f fzf.exe
|
||||||
else ifeq ($(GOOS),linux)
|
else ifeq ($(GOOS),linux)
|
||||||
release: target/$(BINARY32) target/$(BINARY64) target/$(BINARYARM5) target/$(BINARYARM6) target/$(BINARYARM7) target/$(BINARYARM8) target/$(BINARYPPC64LE)
|
release: target/$(BINARY64) target/$(BINARYARM5) target/$(BINARYARM6) target/$(BINARYARM7) target/$(BINARYARM8) target/$(BINARYPPC64LE)
|
||||||
cd target && cp -f $(BINARY32) fzf && tar -czf $(RELEASE32).tgz fzf
|
|
||||||
cd target && cp -f $(BINARY64) fzf && tar -czf $(RELEASE64).tgz fzf
|
cd target && cp -f $(BINARY64) fzf && tar -czf $(RELEASE64).tgz fzf
|
||||||
cd target && cp -f $(BINARYARM5) fzf && tar -czf $(RELEASEARM5).tgz fzf
|
cd target && cp -f $(BINARYARM5) fzf && tar -czf $(RELEASEARM5).tgz fzf
|
||||||
cd target && cp -f $(BINARYARM6) fzf && tar -czf $(RELEASEARM6).tgz fzf
|
cd target && cp -f $(BINARYARM6) fzf && tar -czf $(RELEASEARM6).tgz fzf
|
||||||
@@ -71,8 +63,7 @@ release: target/$(BINARY32) target/$(BINARY64) target/$(BINARYARM5) target/$(BIN
|
|||||||
cd target && cp -f $(BINARYPPC64LE) fzf && tar -czf $(RELEASEPPC64LE).tgz fzf
|
cd target && cp -f $(BINARYPPC64LE) fzf && tar -czf $(RELEASEPPC64LE).tgz fzf
|
||||||
cd target && rm -f fzf
|
cd target && rm -f fzf
|
||||||
else
|
else
|
||||||
release: target/$(BINARY32) target/$(BINARY64)
|
release: target/$(BINARY64)
|
||||||
cd target && cp -f $(BINARY32) fzf && tar -czf $(RELEASE32).tgz fzf
|
|
||||||
cd target && cp -f $(BINARY64) fzf && tar -czf $(RELEASE64).tgz fzf
|
cd target && cp -f $(BINARY64) fzf && tar -czf $(RELEASE64).tgz fzf
|
||||||
cd target && rm -f fzf
|
cd target && rm -f fzf
|
||||||
endif
|
endif
|
||||||
@@ -96,9 +87,6 @@ install: bin/fzf
|
|||||||
clean:
|
clean:
|
||||||
$(RM) -r target
|
$(RM) -r target
|
||||||
|
|
||||||
target/$(BINARY32): $(SOURCES)
|
|
||||||
GOARCH=386 $(GO) build $(BUILD_FLAGS) -o $@
|
|
||||||
|
|
||||||
target/$(BINARY64): $(SOURCES)
|
target/$(BINARY64): $(SOURCES)
|
||||||
GOARCH=amd64 $(GO) build $(BUILD_FLAGS) -o $@
|
GOARCH=amd64 $(GO) build $(BUILD_FLAGS) -o $@
|
||||||
|
|
||||||
|
156
README-VIM.md
156
README-VIM.md
@@ -1,6 +1,50 @@
|
|||||||
FZF Vim integration
|
FZF Vim integration
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Once you have fzf installed, you can enable it inside Vim simply by adding the
|
||||||
|
directory to `&runtimepath` in your Vim configuration file. The path may
|
||||||
|
differ depending on the package manager.
|
||||||
|
|
||||||
|
```vim
|
||||||
|
" If installed using Homebrew
|
||||||
|
set rtp+=/usr/local/opt/fzf
|
||||||
|
|
||||||
|
" If installed using git
|
||||||
|
set rtp+=~/.fzf
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use [vim-plug](https://github.com/junegunn/vim-plug), the same can be
|
||||||
|
written as:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
" If installed using Homebrew
|
||||||
|
Plug '/usr/local/opt/fzf'
|
||||||
|
|
||||||
|
" If installed using git
|
||||||
|
Plug '~/.fzf'
|
||||||
|
```
|
||||||
|
|
||||||
|
But if you want the latest Vim plugin file from GitHub rather than the one
|
||||||
|
included in the package, write:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
Plug 'junegunn/fzf'
|
||||||
|
```
|
||||||
|
|
||||||
|
The Vim plugin will pick up fzf binary available on the system. If fzf is not
|
||||||
|
found on `$PATH`, it will ask you if it should download the latest binary for
|
||||||
|
you.
|
||||||
|
|
||||||
|
To make sure that you have the latest version of the binary, set up
|
||||||
|
post-update hook like so:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
|
||||||
|
```
|
||||||
|
|
||||||
Summary
|
Summary
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@@ -83,8 +127,9 @@ let g:fzf_action = {
|
|||||||
\ 'ctrl-v': 'vsplit' }
|
\ 'ctrl-v': 'vsplit' }
|
||||||
|
|
||||||
" Default fzf layout
|
" Default fzf layout
|
||||||
" - down / up / left / right
|
" - down / up / left / right / window
|
||||||
let g:fzf_layout = { 'down': '~40%' }
|
let g:fzf_layout = { 'down': '40%' }
|
||||||
|
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||||
|
|
||||||
" You can set up fzf window using a Vim command (Neovim or latest Vim 8 required)
|
" You can set up fzf window using a Vim command (Neovim or latest Vim 8 required)
|
||||||
let g:fzf_layout = { 'window': 'enew' }
|
let g:fzf_layout = { 'window': 'enew' }
|
||||||
@@ -115,6 +160,53 @@ let g:fzf_colors =
|
|||||||
let g:fzf_history_dir = '~/.local/share/fzf-history'
|
let g:fzf_history_dir = '~/.local/share/fzf-history'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### Explanation of `g:fzf_colors`
|
||||||
|
|
||||||
|
`g:fzf_colors` is a dictionary mapping fzf elements to a color specification
|
||||||
|
list:
|
||||||
|
|
||||||
|
element: [ component, group1 [, group2, ...] ]
|
||||||
|
|
||||||
|
- `element` is an fzf element to apply a color to:
|
||||||
|
|
||||||
|
| Element | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `fg` / `bg` / `hl` | Item (foreground / background / highlight) |
|
||||||
|
| `fg+` / `bg+` / `hl+` | Current item (foreground / background / highlight) |
|
||||||
|
| `hl` / `hl+` | Highlighted substrings (normal / current) |
|
||||||
|
| `gutter` | Background of the gutter on the left |
|
||||||
|
| `pointer` | Pointer to the current line (`>`) |
|
||||||
|
| `marker` | Multi-select marker (`>`) |
|
||||||
|
| `border` | Border around the window (`--border` and `--preview`) |
|
||||||
|
| `header` | Header (`--header` or `--header-lines`) |
|
||||||
|
| `info` | Info line (match counters) |
|
||||||
|
| `spinner` | Streaming input indicator |
|
||||||
|
| `prompt` | Prompt before query (`> `) |
|
||||||
|
|
||||||
|
- `component` specifies the component (`fg` / `bg`) from which to extract the
|
||||||
|
color when considering each of the following highlight groups
|
||||||
|
|
||||||
|
- `group1 [, group2, ...]` is a list of highlight groups that are searched (in
|
||||||
|
order) for a matching color definition
|
||||||
|
|
||||||
|
For example, consider the following specification:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
'prompt': ['fg', 'Conditional', 'Comment'],
|
||||||
|
```
|
||||||
|
|
||||||
|
This means we color the **prompt**
|
||||||
|
- using the `fg` attribute of the `Conditional` if it exists,
|
||||||
|
- otherwise use the `fg` attribute of the `Comment` highlight group if it exists,
|
||||||
|
- otherwise fall back to the default color settings for the **prompt**.
|
||||||
|
|
||||||
|
You can examine the color option generated according the setting by printing
|
||||||
|
the result of `fzf#wrap()` function like so:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
:echo fzf#wrap()
|
||||||
|
```
|
||||||
|
|
||||||
`fzf#run`
|
`fzf#run`
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@@ -183,7 +275,9 @@ The following table summarizes the available options.
|
|||||||
| `options` | string/list | Options to fzf |
|
| `options` | string/list | Options to fzf |
|
||||||
| `dir` | string | Working directory |
|
| `dir` | string | Working directory |
|
||||||
| `up`/`down`/`left`/`right` | number/string | (Layout) Window position and size (e.g. `20`, `50%`) |
|
| `up`/`down`/`left`/`right` | number/string | (Layout) Window position and size (e.g. `20`, `50%`) |
|
||||||
|
| `tmux` | string | (Layout) fzf-tmux options (e.g. `-p90%,60%`) |
|
||||||
| `window` (Vim 8 / Neovim) | string | (Layout) Command to open fzf window (e.g. `vertical aboveleft 30new`) |
|
| `window` (Vim 8 / Neovim) | string | (Layout) Command to open fzf window (e.g. `vertical aboveleft 30new`) |
|
||||||
|
| `window` (Vim 8 / Neovim) | dict | (Layout) Popup window settings (e.g. `{'width': 0.9, 'height': 0.6}`) |
|
||||||
|
|
||||||
`options` entry can be either a string or a list. For simple cases, string
|
`options` entry can be either a string or a list. For simple cases, string
|
||||||
should suffice, but prefer to use list type to avoid escaping issues.
|
should suffice, but prefer to use list type to avoid escaping issues.
|
||||||
@@ -193,6 +287,19 @@ call fzf#run({'options': '--reverse --prompt "C:\\Program Files\\"'})
|
|||||||
call fzf#run({'options': ['--reverse', '--prompt', 'C:\Program Files\']})
|
call fzf#run({'options': ['--reverse', '--prompt', 'C:\Program Files\']})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
When `window` entry is a dictionary, fzf will start in a popup window. The
|
||||||
|
following options are allowed:
|
||||||
|
|
||||||
|
- Required:
|
||||||
|
- `width` [float range [0 ~ 1]] or [integer range [8 ~ ]]
|
||||||
|
- `height` [float range [0 ~ 1]] or [integer range [4 ~ ]]
|
||||||
|
- Optional:
|
||||||
|
- `yoffset` [float default 0.5 range [0 ~ 1]]
|
||||||
|
- `xoffset` [float default 0.5 range [0 ~ 1]]
|
||||||
|
- `highlight` [string default `'Comment'`]: Highlight group for border
|
||||||
|
- `border` [string default `rounded`]: Border style
|
||||||
|
- `rounded` / `sharp` / `horizontal` / `vertical` / `top` / `bottom` / `left` / `right`
|
||||||
|
|
||||||
`fzf#wrap`
|
`fzf#wrap`
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@@ -276,29 +383,32 @@ The latest versions of Vim and Neovim include builtin terminal emulator
|
|||||||
- On Terminal Vim with a non-default layout
|
- On Terminal Vim with a non-default layout
|
||||||
- `call fzf#run({'left': '30%'})` or `let g:fzf_layout = {'left': '30%'}`
|
- `call fzf#run({'left': '30%'})` or `let g:fzf_layout = {'left': '30%'}`
|
||||||
|
|
||||||
#### Starting fzf in Neovim floating window
|
#### Starting fzf in a popup window
|
||||||
|
|
||||||
```vim
|
```vim
|
||||||
" Using floating windows of Neovim to start fzf
|
" Required:
|
||||||
if has('nvim')
|
" - width [float range [0 ~ 1]] or [integer range [8 ~ ]]
|
||||||
let $FZF_DEFAULT_OPTS .= ' --border --margin=0,2'
|
" - height [float range [0 ~ 1]] or [integer range [4 ~ ]]
|
||||||
|
"
|
||||||
|
" Optional:
|
||||||
|
" - xoffset [float default 0.5 range [0 ~ 1]]
|
||||||
|
" - yoffset [float default 0.5 range [0 ~ 1]]
|
||||||
|
" - highlight [string default 'Comment']: Highlight group for border
|
||||||
|
" - border [string default 'rounded']: Border style
|
||||||
|
" - 'rounded' / 'sharp' / 'horizontal' / 'vertical' / 'top' / 'bottom' / 'left' / 'right'
|
||||||
|
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||||
|
```
|
||||||
|
|
||||||
function! FloatingFZF()
|
Alternatively, you can make fzf open in a tmux popup window (requires tmux 3.2
|
||||||
let width = float2nr(&columns * 0.9)
|
or above) by putting fzf-tmux options in `tmux` key.
|
||||||
let height = float2nr(&lines * 0.6)
|
|
||||||
let opts = { 'relative': 'editor',
|
|
||||||
\ 'row': (&lines - height) / 2,
|
|
||||||
\ 'col': (&columns - width) / 2,
|
|
||||||
\ 'width': width,
|
|
||||||
\ 'height': height }
|
|
||||||
|
|
||||||
let win = nvim_open_win(nvim_create_buf(v:false, v:true), v:true, opts)
|
```vim
|
||||||
call setwinvar(win, '&winhighlight', 'NormalFloat:Normal')
|
" See `man fzf-tmux` for available options
|
||||||
endfunction
|
if exists('$TMUX')
|
||||||
|
let g:fzf_layout = { 'tmux': '-p90%,60%' }
|
||||||
let g:fzf_layout = { 'window': 'call FloatingFZF()' }
|
else
|
||||||
|
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||||
endif
|
endif
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Hide statusline
|
#### Hide statusline
|
||||||
@@ -307,8 +417,8 @@ When fzf starts in a terminal buffer, the file type of the buffer is set to
|
|||||||
`fzf`. So you can set up `FileType fzf` autocmd to customize the settings of
|
`fzf`. So you can set up `FileType fzf` autocmd to customize the settings of
|
||||||
the window.
|
the window.
|
||||||
|
|
||||||
For example, if you use the default layout (`{'down': '~40%'}`) on Neovim, you
|
For example, if you use a non-popup layout (e.g. `{'down': '40%'}`) on Neovim,
|
||||||
might want to temporarily disable the statusline for a cleaner look.
|
you might want to temporarily disable the statusline for a cleaner look.
|
||||||
|
|
||||||
```vim
|
```vim
|
||||||
if has('nvim') && !exists('g:fzf_layout')
|
if has('nvim') && !exists('g:fzf_layout')
|
||||||
@@ -323,4 +433,4 @@ endif
|
|||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2019 Junegunn Choi
|
Copyright (c) 2013-2020 Junegunn Choi
|
||||||
|
222
README.md
222
README.md
@@ -22,6 +22,8 @@ Pros
|
|||||||
Table of Contents
|
Table of Contents
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
<!-- vim-markdown-toc GFM -->
|
||||||
|
|
||||||
* [Installation](#installation)
|
* [Installation](#installation)
|
||||||
* [Using Homebrew or Linuxbrew](#using-homebrew-or-linuxbrew)
|
* [Using Homebrew or Linuxbrew](#using-homebrew-or-linuxbrew)
|
||||||
* [Using git](#using-git)
|
* [Using git](#using-git)
|
||||||
@@ -38,8 +40,8 @@ Table of Contents
|
|||||||
* [Options](#options)
|
* [Options](#options)
|
||||||
* [Demo](#demo)
|
* [Demo](#demo)
|
||||||
* [Examples](#examples)
|
* [Examples](#examples)
|
||||||
* [fzf-tmux script](#fzf-tmux-script)
|
* [`fzf-tmux` script](#fzf-tmux-script)
|
||||||
* [Key bindings for command line](#key-bindings-for-command-line)
|
* [Key bindings for command-line](#key-bindings-for-command-line)
|
||||||
* [Fuzzy completion for bash and zsh](#fuzzy-completion-for-bash-and-zsh)
|
* [Fuzzy completion for bash and zsh](#fuzzy-completion-for-bash-and-zsh)
|
||||||
* [Files and directories](#files-and-directories)
|
* [Files and directories](#files-and-directories)
|
||||||
* [Process IDs](#process-ids)
|
* [Process IDs](#process-ids)
|
||||||
@@ -47,16 +49,23 @@ Table of Contents
|
|||||||
* [Environment variables / Aliases](#environment-variables--aliases)
|
* [Environment variables / Aliases](#environment-variables--aliases)
|
||||||
* [Settings](#settings)
|
* [Settings](#settings)
|
||||||
* [Supported commands](#supported-commands)
|
* [Supported commands](#supported-commands)
|
||||||
|
* [Custom fuzzy completion](#custom-fuzzy-completion)
|
||||||
* [Vim plugin](#vim-plugin)
|
* [Vim plugin](#vim-plugin)
|
||||||
* [Advanced topics](#advanced-topics)
|
* [Advanced topics](#advanced-topics)
|
||||||
* [Performance](#performance)
|
* [Performance](#performance)
|
||||||
* [Executing external programs](#executing-external-programs)
|
* [Executing external programs](#executing-external-programs)
|
||||||
|
* [Reloading the candidate list](#reloading-the-candidate-list)
|
||||||
|
* [1. Update the list of processes by pressing CTRL-R](#1-update-the-list-of-processes-by-pressing-ctrl-r)
|
||||||
|
* [2. Switch between sources by pressing CTRL-D or CTRL-F](#2-switch-between-sources-by-pressing-ctrl-d-or-ctrl-f)
|
||||||
|
* [3. Interactive ripgrep integration](#3-interactive-ripgrep-integration)
|
||||||
* [Preview window](#preview-window)
|
* [Preview window](#preview-window)
|
||||||
* [Tips](#tips)
|
* [Tips](#tips)
|
||||||
* [Respecting .gitignore](#respecting-gitignore)
|
* [Respecting `.gitignore`](#respecting-gitignore)
|
||||||
* [Fish shell](#fish-shell)
|
* [Fish shell](#fish-shell)
|
||||||
* [Related projects](#related-projects)
|
* [Related projects](#related-projects)
|
||||||
* [<a href="LICENSE">License</a>](#license)
|
* [License](#license)
|
||||||
|
|
||||||
|
<!-- vim-markdown-toc -->
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
@@ -103,15 +112,18 @@ git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
|
|||||||
|
|
||||||
### Using Linux package managers
|
### Using Linux package managers
|
||||||
|
|
||||||
| Distro | Command |
|
| Package Manager | Linux Distribution | Command |
|
||||||
| --- | --- |
|
| --- | --- | --- |
|
||||||
| Alpine Linux | `sudo apk add fzf` |
|
| APK | Alpine Linux | `sudo apk add fzf` |
|
||||||
| Arch Linux | `sudo pacman -S fzf` |
|
| APT | Debian 9+/Ubuntu 19.10+ | `sudo apt-get install fzf` |
|
||||||
| Debian | `sudo apt-get install fzf` |
|
| Conda | | `conda install -c conda-forge fzf` |
|
||||||
| Fedora | `sudo dnf install fzf` |
|
| DNF | Fedora | `sudo dnf install fzf` |
|
||||||
| FreeBSD | `pkg install fzf` |
|
| Nix | NixOS, etc. | `nix-env -iA nixpkgs.fzf` |
|
||||||
| NixOS | `nix-env -iA nixpkgs.fzf` |
|
| Pacman | Arch Linux | `sudo pacman -S fzf` |
|
||||||
| openSUSE | `sudo zypper install fzf` |
|
| pkg | FreeBSD | `pkg install fzf` |
|
||||||
|
| pkg_add | OpenBSD | `pkg_add fzf` |
|
||||||
|
| XBPS | Void Linux | `sudo xbps-install -S fzf` |
|
||||||
|
| Zypper | openSUSE | `sudo zypper install fzf` |
|
||||||
|
|
||||||
Shell extensions (key bindings and fuzzy auto-completion) and Vim/Neovim
|
Shell extensions (key bindings and fuzzy auto-completion) and Vim/Neovim
|
||||||
plugin may or may not be enabled by default depending on the package manager.
|
plugin may or may not be enabled by default depending on the package manager.
|
||||||
@@ -137,39 +149,18 @@ page][windows-wiki].
|
|||||||
|
|
||||||
### As Vim plugin
|
### As Vim plugin
|
||||||
|
|
||||||
Once you have fzf installed, you can enable it inside Vim simply by adding the
|
If you use
|
||||||
directory to `&runtimepath` in your Vim configuration file. The path may
|
[vim-plug](https://github.com/junegunn/vim-plug), add this line to your Vim
|
||||||
differ depending on the package manager.
|
configuration file:
|
||||||
|
|
||||||
```vim
|
```vim
|
||||||
" If installed using Homebrew
|
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
|
||||||
set rtp+=/usr/local/opt/fzf
|
|
||||||
|
|
||||||
" If installed using git
|
|
||||||
set rtp+=~/.fzf
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If you use [vim-plug](https://github.com/junegunn/vim-plug), the same can be
|
`fzf#install()` makes sure that you have the latest binary, but it's optional,
|
||||||
written as:
|
so you can omit it if you use a plugin manager that doesn't support hooks.
|
||||||
|
|
||||||
```vim
|
For more installation options, see [README-VIM.md](README-VIM.md).
|
||||||
" If installed using Homebrew
|
|
||||||
Plug '/usr/local/opt/fzf'
|
|
||||||
|
|
||||||
" If installed using git
|
|
||||||
Plug '~/.fzf'
|
|
||||||
```
|
|
||||||
|
|
||||||
But instead of separately installing fzf on your system (using Homebrew or
|
|
||||||
"git clone") and enabling it on Vim (adding it to `&runtimepath`), you can use
|
|
||||||
vim-plug to do both.
|
|
||||||
|
|
||||||
```vim
|
|
||||||
" PlugInstall and PlugUpdate will clone fzf in ~/.fzf and run the install script
|
|
||||||
Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
|
|
||||||
" Both options are optional. You don't have to install fzf in ~/.fzf
|
|
||||||
" and you don't have to run the install script if you use fzf only in Vim.
|
|
||||||
```
|
|
||||||
|
|
||||||
Upgrading fzf
|
Upgrading fzf
|
||||||
-------------
|
-------------
|
||||||
@@ -180,6 +171,7 @@ method used.
|
|||||||
|
|
||||||
- git: `cd ~/.fzf && git pull && ./install`
|
- git: `cd ~/.fzf && git pull && ./install`
|
||||||
- brew: `brew update; brew reinstall fzf`
|
- brew: `brew update; brew reinstall fzf`
|
||||||
|
- macports: `sudo port upgrade fzf`
|
||||||
- chocolatey: `choco upgrade fzf`
|
- chocolatey: `choco upgrade fzf`
|
||||||
- vim-plug: `:PlugUpdate fzf`
|
- vim-plug: `:PlugUpdate fzf`
|
||||||
|
|
||||||
@@ -299,8 +291,10 @@ own as well.
|
|||||||
[fzf-tmux](bin/fzf-tmux) is a bash script that opens fzf in a tmux pane.
|
[fzf-tmux](bin/fzf-tmux) is a bash script that opens fzf in a tmux pane.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# usage: fzf-tmux [-u|-d [HEIGHT[%]]] [-l|-r [WIDTH[%]]] [--] [FZF OPTIONS]
|
# usage: fzf-tmux [LAYOUT OPTIONS] [--] [FZF OPTIONS]
|
||||||
# (-[udlr]: up/down/left/right)
|
|
||||||
|
# See available options
|
||||||
|
fzf-tmux --help
|
||||||
|
|
||||||
# select git branches in horizontal split below (15 lines)
|
# select git branches in horizontal split below (15 lines)
|
||||||
git branch | fzf-tmux -d 15
|
git branch | fzf-tmux -d 15
|
||||||
@@ -309,7 +303,7 @@ git branch | fzf-tmux -d 15
|
|||||||
cat /usr/share/dict/words | fzf-tmux -l 20% --multi --reverse
|
cat /usr/share/dict/words | fzf-tmux -l 20% --multi --reverse
|
||||||
```
|
```
|
||||||
|
|
||||||
It will still work even when you're not on tmux, silently ignoring `-[udlr]`
|
It will still work even when you're not on tmux, silently ignoring `-[pudlr]`
|
||||||
options, so you can invariably use `fzf-tmux` in your scripts.
|
options, so you can invariably use `fzf-tmux` in your scripts.
|
||||||
|
|
||||||
Alternatively, you can use `--height HEIGHT[%]` option not to start fzf in
|
Alternatively, you can use `--height HEIGHT[%]` option not to start fzf in
|
||||||
@@ -336,13 +330,9 @@ fish.
|
|||||||
- Set `FZF_ALT_C_COMMAND` to override the default command
|
- Set `FZF_ALT_C_COMMAND` to override the default command
|
||||||
- Set `FZF_ALT_C_OPTS` to pass additional options
|
- Set `FZF_ALT_C_OPTS` to pass additional options
|
||||||
|
|
||||||
If you're on a tmux session, you can start fzf in a split pane by setting
|
If you're on a tmux session, you can start fzf in a tmux split pane or in
|
||||||
`FZF_TMUX` to 1, and change the height of the pane with `FZF_TMUX_HEIGHT`
|
a tmux popup window by setting `FZF_TMUX_OPTS` (e.g. `-d 40%`).
|
||||||
(e.g. `20`, `50%`).
|
See `fzf-tmux --help` for available options.
|
||||||
|
|
||||||
If you use vi mode on bash, you need to add `set -o vi` *before* `source
|
|
||||||
~/.fzf.bash` in your .bashrc, so that it correctly sets up key bindings for vi
|
|
||||||
mode.
|
|
||||||
|
|
||||||
More tips can be found on [the wiki page](https://github.com/junegunn/fzf/wiki/Configuring-shell-key-bindings).
|
More tips can be found on [the wiki page](https://github.com/junegunn/fzf/wiki/Configuring-shell-key-bindings).
|
||||||
|
|
||||||
@@ -427,6 +417,21 @@ _fzf_compgen_path() {
|
|||||||
_fzf_compgen_dir() {
|
_fzf_compgen_dir() {
|
||||||
fd --type d --hidden --follow --exclude ".git" . "$1"
|
fd --type d --hidden --follow --exclude ".git" . "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# (EXPERIMENTAL) Advanced customization of fzf options via _fzf_comprun function
|
||||||
|
# - The first argument to the function is the name of the command.
|
||||||
|
# - You should make sure to pass the rest of the arguments to fzf.
|
||||||
|
_fzf_comprun() {
|
||||||
|
local command=$1
|
||||||
|
shift
|
||||||
|
|
||||||
|
case "$command" in
|
||||||
|
cd) fzf "$@" --preview 'tree -C {} | head -200' ;;
|
||||||
|
export|unset) fzf "$@" --preview "eval 'echo \$'{}" ;;
|
||||||
|
ssh) fzf "$@" --preview 'dig {}' ;;
|
||||||
|
*) fzf "$@" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Supported commands
|
#### Supported commands
|
||||||
@@ -436,11 +441,61 @@ On bash, fuzzy completion is enabled only for a predefined set of commands
|
|||||||
commands as well by using `_fzf_setup_completion` helper function.
|
commands as well by using `_fzf_setup_completion` helper function.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# usage: _fzf_setup_completion path|dir COMMANDS...
|
# usage: _fzf_setup_completion path|dir|var|alias|host COMMANDS...
|
||||||
_fzf_setup_completion path ag git kubectl
|
_fzf_setup_completion path ag git kubectl
|
||||||
_fzf_setup_completion dir tree
|
_fzf_setup_completion dir tree
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Custom fuzzy completion
|
||||||
|
|
||||||
|
_**(Custom completion API is experimental and subject to change)**_
|
||||||
|
|
||||||
|
For a command named _"COMMAND"_, define `_fzf_complete_COMMAND` function using
|
||||||
|
`_fzf_complete` helper.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Custom fuzzy completion for "doge" command
|
||||||
|
# e.g. doge **<TAB>
|
||||||
|
_fzf_complete_doge() {
|
||||||
|
_fzf_complete --multi --reverse --prompt="doge> " -- "$@" < <(
|
||||||
|
echo very
|
||||||
|
echo wow
|
||||||
|
echo such
|
||||||
|
echo doge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- The arguments before `--` are the options to fzf.
|
||||||
|
- After `--`, simply pass the original completion arguments unchanged (`"$@"`).
|
||||||
|
- Then write a set of commands that generates the completion candidates and
|
||||||
|
feed its output to the function using process substitution (`< <(...)`).
|
||||||
|
|
||||||
|
zsh will automatically pick up the function using the naming convention but in
|
||||||
|
bash you have to manually associate the function with the command using
|
||||||
|
`complete` command.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[ -n "$BASH" ] && complete -F _fzf_complete_doge -o default -o bashdefault doge
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to post-process the output from fzf, define
|
||||||
|
`_fzf_complete_COMMAND_post` as follows.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
_fzf_complete_foo() {
|
||||||
|
_fzf_complete --multi --reverse --header-lines=3 -- "$@" < <(
|
||||||
|
ls -al
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_foo_post() {
|
||||||
|
awk '{print $NF}'
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -n "$BASH" ] && complete -F _fzf_complete_foo -o default -o bashdefault foo
|
||||||
|
```
|
||||||
|
|
||||||
Vim plugin
|
Vim plugin
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@@ -481,10 +536,56 @@ fzf --bind 'f1:execute(less -f {}),ctrl-y:execute-silent(echo {} | pbcopy)+abort
|
|||||||
|
|
||||||
See *KEY BINDINGS* section of the man page for details.
|
See *KEY BINDINGS* section of the man page for details.
|
||||||
|
|
||||||
|
### Reloading the candidate list
|
||||||
|
|
||||||
|
By binding `reload` action to a key or an event, you can make fzf dynamically
|
||||||
|
reload the candidate list. See https://github.com/junegunn/fzf/issues/1750 for
|
||||||
|
more details.
|
||||||
|
|
||||||
|
#### 1. Update the list of processes by pressing CTRL-R
|
||||||
|
|
||||||
|
```sh
|
||||||
|
FZF_DEFAULT_COMMAND='ps -ef' \
|
||||||
|
fzf --bind 'ctrl-r:reload($FZF_DEFAULT_COMMAND)' \
|
||||||
|
--header 'Press CTRL-R to reload' --header-lines=1 \
|
||||||
|
--height=50% --layout=reverse
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Switch between sources by pressing CTRL-D or CTRL-F
|
||||||
|
|
||||||
|
```sh
|
||||||
|
FZF_DEFAULT_COMMAND='find . -type f' \
|
||||||
|
fzf --bind 'ctrl-d:reload(find . -type d),ctrl-f:reload($FZF_DEFAULT_COMMAND)' \
|
||||||
|
--height=50% --layout=reverse
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Interactive ripgrep integration
|
||||||
|
|
||||||
|
The following example uses fzf as the selector interface for ripgrep. We bound
|
||||||
|
`reload` action to `change` event, so every time you type on fzf, ripgrep
|
||||||
|
process will restart with the updated query string denoted by the placeholder
|
||||||
|
expression `{q}`. Also, note that we used `--phony` option so that fzf doesn't
|
||||||
|
perform any secondary filtering.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
INITIAL_QUERY=""
|
||||||
|
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
|
||||||
|
FZF_DEFAULT_COMMAND="$RG_PREFIX '$INITIAL_QUERY'" \
|
||||||
|
fzf --bind "change:reload:$RG_PREFIX {q} || true" \
|
||||||
|
--ansi --phony --query "$INITIAL_QUERY" \
|
||||||
|
--height=50% --layout=reverse
|
||||||
|
```
|
||||||
|
|
||||||
|
If ripgrep doesn't find any matches, it will exit with a non-zero exit status,
|
||||||
|
and fzf will warn you about it. To suppress the warning message, we added
|
||||||
|
`|| true` to the command, so that it always exits with 0.
|
||||||
|
|
||||||
### Preview window
|
### Preview window
|
||||||
|
|
||||||
When `--preview` option is set, fzf automatically starts an external process with
|
When the `--preview` option is set, fzf automatically starts an external process
|
||||||
the current line as the argument and shows the result in the split window.
|
with the current line as the argument and shows the result in the split window.
|
||||||
|
Your `$SHELL` is used to execute the command with `$SHELL -c COMMAND`.
|
||||||
|
The window can be scrolled using the mouse or custom key bindings.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# {} is replaced to the single-quoted string of the focused line
|
# {} is replaced to the single-quoted string of the focused line
|
||||||
@@ -500,13 +601,12 @@ fzf --preview 'head -100 {}'
|
|||||||
```
|
```
|
||||||
|
|
||||||
Preview window supports ANSI colors, so you can use any program that
|
Preview window supports ANSI colors, so you can use any program that
|
||||||
syntax-highlights the content of a file.
|
syntax-highlights the content of a file, such as
|
||||||
|
[Bat](https://github.com/sharkdp/bat) or
|
||||||
- Bat: https://github.com/sharkdp/bat
|
[Highlight](http://www.andre-simon.de/doku/highlight/en/highlight.php):
|
||||||
- Highlight: http://www.andre-simon.de/doku/highlight/en/highlight.php
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
fzf --preview 'bat --style=numbers --color=always {} | head -500'
|
fzf --preview 'bat --style=numbers --color=always --line-range :500 {}'
|
||||||
```
|
```
|
||||||
|
|
||||||
You can customize the size, position, and border of the preview window using
|
You can customize the size, position, and border of the preview window using
|
||||||
@@ -535,7 +635,7 @@ not a good idea to add `--preview` option to your `$FZF_DEFAULT_OPTS`**.
|
|||||||
# *********************
|
# *********************
|
||||||
# ** DO NOT DO THIS! **
|
# ** DO NOT DO THIS! **
|
||||||
# *********************
|
# *********************
|
||||||
export FZF_DEFAULT_OPTS='--preview "bat --style=numbers --color=always {} | head -500"'
|
export FZF_DEFAULT_OPTS='--preview "bat --style=numbers --color=always --line-range :500 {}"'
|
||||||
|
|
||||||
# bat doesn't work with any input other than the list of files
|
# bat doesn't work with any input other than the list of files
|
||||||
ps -ef | fzf
|
ps -ef | fzf
|
||||||
@@ -605,4 +705,4 @@ https://github.com/junegunn/fzf/wiki/Related-projects
|
|||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2019 Junegunn Choi
|
Copyright (c) 2013-2020 Junegunn Choi
|
||||||
|
103
bin/fzf-tmux
103
bin/fzf-tmux
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# fzf-tmux: starts fzf in a tmux pane
|
# fzf-tmux: starts fzf in a tmux pane
|
||||||
# usage: fzf-tmux [-u|-d [HEIGHT[%]]] [-l|-r [WIDTH[%]]] [--] [FZF OPTIONS]
|
# usage: fzf-tmux [LAYOUT OPTIONS] [--] [FZF OPTIONS]
|
||||||
|
|
||||||
fail() {
|
fail() {
|
||||||
>&2 echo "$1"
|
>&2 echo "$1"
|
||||||
@@ -10,6 +10,7 @@ fail() {
|
|||||||
fzf="$(command -v fzf 2> /dev/null)" || fzf="$(dirname "$0")/fzf"
|
fzf="$(command -v fzf 2> /dev/null)" || fzf="$(dirname "$0")/fzf"
|
||||||
[[ -x "$fzf" ]] || fail 'fzf executable not found'
|
[[ -x "$fzf" ]] || fail 'fzf executable not found'
|
||||||
|
|
||||||
|
tmux_args=()
|
||||||
args=()
|
args=()
|
||||||
opt=""
|
opt=""
|
||||||
skip=""
|
skip=""
|
||||||
@@ -20,15 +21,23 @@ term=""
|
|||||||
[[ -n "$COLUMNS" ]] && columns=$COLUMNS || columns=$(tput cols) || columns=$(tmux display-message -p "#{pane_width}")
|
[[ -n "$COLUMNS" ]] && columns=$COLUMNS || columns=$(tput cols) || columns=$(tmux display-message -p "#{pane_width}")
|
||||||
|
|
||||||
help() {
|
help() {
|
||||||
>&2 echo 'usage: fzf-tmux [-u|-d [HEIGHT[%]]] [-l|-r [WIDTH[%]]] [--] [FZF OPTIONS]
|
>&2 echo 'usage: fzf-tmux [LAYOUT OPTIONS] [--] [FZF OPTIONS]
|
||||||
|
|
||||||
Layout
|
LAYOUT OPTIONS:
|
||||||
|
(default layout: -d 50%)
|
||||||
|
|
||||||
|
Popup window (requires tmux 3.2 or above):
|
||||||
|
-p [WIDTH[%][,HEIGHT[%]]] (default: 50%)
|
||||||
|
-w WIDTH[%]
|
||||||
|
-h HEIGHT[%]
|
||||||
|
-x COL
|
||||||
|
-y ROW
|
||||||
|
|
||||||
|
Split pane:
|
||||||
-u [HEIGHT[%]] Split above (up)
|
-u [HEIGHT[%]] Split above (up)
|
||||||
-d [HEIGHT[%]] Split below (down)
|
-d [HEIGHT[%]] Split below (down)
|
||||||
-l [WIDTH[%]] Split left
|
-l [WIDTH[%]] Split left
|
||||||
-r [WIDTH[%]] Split right
|
-r [WIDTH[%]] Split right
|
||||||
|
|
||||||
(default: -d 50%)
|
|
||||||
'
|
'
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
@@ -47,8 +56,10 @@ while [[ $# -gt 0 ]]; do
|
|||||||
echo "fzf-tmux (with fzf $("$fzf" --version))"
|
echo "fzf-tmux (with fzf $("$fzf" --version))"
|
||||||
exit
|
exit
|
||||||
;;
|
;;
|
||||||
-w*|-h*|-d*|-u*|-r*|-l*)
|
-p*|-w*|-h*|-x*|-y*|-d*|-u*|-r*|-l*)
|
||||||
if [[ "$arg" =~ ^.[lrw] ]]; then
|
if [[ "$arg" =~ ^-[pwhxy] ]]; then
|
||||||
|
[[ "$opt" =~ "-K -E" ]] || opt="-K -E"
|
||||||
|
elif [[ "$arg" =~ ^.[lr] ]]; then
|
||||||
opt="-h"
|
opt="-h"
|
||||||
if [[ "$arg" =~ ^.l ]]; then
|
if [[ "$arg" =~ ^.l ]]; then
|
||||||
opt="$opt -d"
|
opt="$opt -d"
|
||||||
@@ -66,7 +77,7 @@ while [[ $# -gt 0 ]]; do
|
|||||||
if [[ ${#arg} -gt 2 ]]; then
|
if [[ ${#arg} -gt 2 ]]; then
|
||||||
size="${arg:2}"
|
size="${arg:2}"
|
||||||
else
|
else
|
||||||
if [[ "$1" =~ ^[0-9]+%?$ ]]; then
|
if [[ "$1" =~ ^[0-9%,]+$ ]] || [[ "$1" =~ ^[A-Z]$ ]]; then
|
||||||
size="$1"
|
size="$1"
|
||||||
shift
|
shift
|
||||||
else
|
else
|
||||||
@@ -74,7 +85,15 @@ while [[ $# -gt 0 ]]; do
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$size" =~ %$ ]]; then
|
if [[ "$arg" =~ ^-p ]]; then
|
||||||
|
if [[ -n "$size" ]]; then
|
||||||
|
w=${size%%,*}
|
||||||
|
h=${size##*,}
|
||||||
|
opt="$opt -w$w -h$h"
|
||||||
|
fi
|
||||||
|
elif [[ "$arg" =~ ^-[whxy] ]]; then
|
||||||
|
opt="$opt ${arg:0:2}$size"
|
||||||
|
elif [[ "$size" =~ %$ ]]; then
|
||||||
size=${size:0:((${#size}-1))}
|
size=${size:0:((${#size}-1))}
|
||||||
if [[ -n "$swap" ]]; then
|
if [[ -n "$swap" ]]; then
|
||||||
opt="$opt -p $(( 100 - size ))"
|
opt="$opt -p $(( 100 - size ))"
|
||||||
@@ -100,6 +119,8 @@ while [[ $# -gt 0 ]]; do
|
|||||||
# "--" can be used to separate fzf-tmux options from fzf options to
|
# "--" can be used to separate fzf-tmux options from fzf options to
|
||||||
# avoid conflicts
|
# avoid conflicts
|
||||||
skip=1
|
skip=1
|
||||||
|
tmux_args=("${args[@]}")
|
||||||
|
args=()
|
||||||
continue
|
continue
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -109,17 +130,17 @@ while [[ $# -gt 0 ]]; do
|
|||||||
[[ -n "$skip" ]] && args+=("$arg")
|
[[ -n "$skip" ]] && args+=("$arg")
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ -z "$TMUX" || "$opt" =~ ^-h && "$columns" -le 40 || ! "$opt" =~ ^-h && "$lines" -le 15 ]]; then
|
if [[ -z "$TMUX" ]]; then
|
||||||
"$fzf" "${args[@]}"
|
"$fzf" "${args[@]}"
|
||||||
exit $?
|
exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --height option is not allowed
|
# --height option is not allowed
|
||||||
args+=("--no-height")
|
args=("${args[@]}" "--no-height")
|
||||||
|
|
||||||
# Handle zoomed tmux pane by moving it to a temp window
|
# Handle zoomed tmux pane without popup options by moving it to a temp window
|
||||||
if tmux list-panes -F '#F' | grep -q Z; then
|
if [[ ! "$opt" =~ "-K -E" ]] && tmux list-panes -F '#F' | grep -q Z; then
|
||||||
zoomed=1
|
zoomed_without_popup=1
|
||||||
original_window=$(tmux display-message -p "#{window_id}")
|
original_window=$(tmux display-message -p "#{window_id}")
|
||||||
tmp_window=$(tmux new-window -d -P -F "#{window_id}" "bash -c 'while :; do for c in \\| / - '\\;' do sleep 0.2; printf \"\\r\$c fzf-tmux is running\\r\"; done; done'")
|
tmp_window=$(tmux new-window -d -P -F "#{window_id}" "bash -c 'while :; do for c in \\| / - '\\;' do sleep 0.2; printf \"\\r\$c fzf-tmux is running\\r\"; done; done'")
|
||||||
tmux swap-pane -t $tmp_window \; select-window -t $tmp_window
|
tmux swap-pane -t $tmp_window \; select-window -t $tmp_window
|
||||||
@@ -133,16 +154,17 @@ argsf="${TMPDIR:-/tmp}/fzf-args-$id"
|
|||||||
fifo1="${TMPDIR:-/tmp}/fzf-fifo1-$id"
|
fifo1="${TMPDIR:-/tmp}/fzf-fifo1-$id"
|
||||||
fifo2="${TMPDIR:-/tmp}/fzf-fifo2-$id"
|
fifo2="${TMPDIR:-/tmp}/fzf-fifo2-$id"
|
||||||
fifo3="${TMPDIR:-/tmp}/fzf-fifo3-$id"
|
fifo3="${TMPDIR:-/tmp}/fzf-fifo3-$id"
|
||||||
|
tmux_win_opts=( $(tmux show-window-options remain-on-exit \; show-window-options synchronize-panes | sed '/ off/d; s/^/set-window-option /; s/$/ \\;/') )
|
||||||
cleanup() {
|
cleanup() {
|
||||||
\rm -f $argsf $fifo1 $fifo2 $fifo3
|
\rm -f $argsf $fifo1 $fifo2 $fifo3
|
||||||
|
|
||||||
# Restore tmux window options
|
# Restore tmux window options
|
||||||
if [[ "${#tmux_win_opts[@]}" -gt 0 ]]; then
|
if [[ "${#tmux_win_opts[@]}" -gt 0 ]]; then
|
||||||
eval "tmux ${tmux_win_opts[@]}"
|
eval "tmux ${tmux_win_opts[*]}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove temp window if we were zoomed
|
# Remove temp window if we were zoomed without popup options
|
||||||
if [[ -n "$zoomed" ]]; then
|
if [[ -n "$zoomed_without_popup" ]]; then
|
||||||
tmux display-message -p "#{window_id}" > /dev/null
|
tmux display-message -p "#{window_id}" > /dev/null
|
||||||
tmux swap-pane -t $original_window \; \
|
tmux swap-pane -t $original_window \; \
|
||||||
select-window -t $original_window \; \
|
select-window -t $original_window \; \
|
||||||
@@ -158,44 +180,45 @@ cleanup() {
|
|||||||
trap 'cleanup 1' SIGUSR1
|
trap 'cleanup 1' SIGUSR1
|
||||||
trap 'cleanup' EXIT
|
trap 'cleanup' EXIT
|
||||||
|
|
||||||
envs="env TERM=$TERM "
|
envs="export TERM=$TERM "
|
||||||
|
[[ "$opt" =~ "-K -E" ]] && FZF_DEFAULT_OPTS="--margin 0,1 $FZF_DEFAULT_OPTS"
|
||||||
[[ -n "$FZF_DEFAULT_OPTS" ]] && envs="$envs FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS")"
|
[[ -n "$FZF_DEFAULT_OPTS" ]] && envs="$envs FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS")"
|
||||||
[[ -n "$FZF_DEFAULT_COMMAND" ]] && envs="$envs FZF_DEFAULT_COMMAND=$(printf %q "$FZF_DEFAULT_COMMAND")"
|
[[ -n "$FZF_DEFAULT_COMMAND" ]] && envs="$envs FZF_DEFAULT_COMMAND=$(printf %q "$FZF_DEFAULT_COMMAND")"
|
||||||
|
echo "$envs;" > "$argsf"
|
||||||
mkfifo -m o+w $fifo2
|
|
||||||
mkfifo -m o+w $fifo3
|
|
||||||
|
|
||||||
# Build arguments to fzf
|
# Build arguments to fzf
|
||||||
opts=""
|
opts=$(printf "%q " "${args[@]}")
|
||||||
for arg in "${args[@]}"; do
|
|
||||||
arg="${arg//\\/\\\\}"
|
|
||||||
arg="${arg//\"/\\\"}"
|
|
||||||
arg="${arg//\`/\\\`}"
|
|
||||||
arg="${arg//$/\\$}"
|
|
||||||
opts="$opts \"$arg\""
|
|
||||||
done
|
|
||||||
|
|
||||||
pppid=$$
|
pppid=$$
|
||||||
echo -n "trap 'kill -SIGUSR1 -$pppid' EXIT SIGINT SIGTERM;" > $argsf
|
echo -n "trap 'kill -SIGUSR1 -$pppid' EXIT SIGINT SIGTERM;" >> $argsf
|
||||||
close="; trap - EXIT SIGINT SIGTERM $close"
|
close="; trap - EXIT SIGINT SIGTERM $close"
|
||||||
|
|
||||||
tmux_win_opts=( $(tmux show-window-options remain-on-exit \; show-window-options synchronize-panes | sed '/ off/d; s/^/set-window-option /; s/$/ \\;/') )
|
export TMUX=$(cut -d , -f 1,2 <<< "$TMUX")
|
||||||
|
mkfifo -m o+w $fifo2
|
||||||
|
if [[ "$opt" =~ "-K -E" ]]; then
|
||||||
|
cat $fifo2 &
|
||||||
|
if [[ -n "$term" ]] || [[ -t 0 ]]; then
|
||||||
|
cat <<< "\"$fzf\" $opts > $fifo2; out=\$? $close; exit \$out" >> $argsf
|
||||||
|
else
|
||||||
|
mkfifo $fifo1
|
||||||
|
cat <<< "\"$fzf\" $opts < $fifo1 > $fifo2; out=\$? $close; exit \$out" >> $argsf
|
||||||
|
cat <&0 > $fifo1 &
|
||||||
|
fi
|
||||||
|
tmux popup -d "$PWD" "${tmux_args[@]}" $opt -R "bash $argsf" > /dev/null 2>&1
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkfifo -m o+w $fifo3
|
||||||
if [[ -n "$term" ]] || [[ -t 0 ]]; then
|
if [[ -n "$term" ]] || [[ -t 0 ]]; then
|
||||||
cat <<< "\"$fzf\" $opts > $fifo2; echo \$? > $fifo3 $close" >> $argsf
|
cat <<< "\"$fzf\" $opts > $fifo2; echo \$? > $fifo3 $close" >> $argsf
|
||||||
TMUX=$(echo $TMUX | cut -d , -f 1,2) tmux set-window-option synchronize-panes off \;\
|
|
||||||
set-window-option remain-on-exit off \;\
|
|
||||||
split-window $opt "$envs bash -c 'cd $(printf %q "$PWD"); exec -a fzf bash $argsf'" $swap \
|
|
||||||
> /dev/null 2>&1
|
|
||||||
else
|
else
|
||||||
mkfifo $fifo1
|
mkfifo $fifo1
|
||||||
cat <<< "\"$fzf\" $opts < $fifo1 > $fifo2; echo \$? > $fifo3 $close" >> $argsf
|
cat <<< "\"$fzf\" $opts < $fifo1 > $fifo2; echo \$? > $fifo3 $close" >> $argsf
|
||||||
TMUX=$(echo $TMUX | cut -d , -f 1,2) tmux set-window-option synchronize-panes off \;\
|
|
||||||
set-window-option remain-on-exit off \;\
|
|
||||||
split-window $opt "$envs bash -c 'exec -a fzf bash $argsf'" $swap \
|
|
||||||
> /dev/null 2>&1
|
|
||||||
cat <&0 > $fifo1 &
|
cat <&0 > $fifo1 &
|
||||||
fi
|
fi
|
||||||
|
tmux set-window-option synchronize-panes off \;\
|
||||||
|
set-window-option remain-on-exit off \;\
|
||||||
|
split-window -c "$PWD" $opt "${tmux_args[@]}" "bash -c 'exec -a fzf bash $argsf'" $swap \
|
||||||
|
> /dev/null 2>&1 || { "$fzf" "${args[@]}"; exit $?; }
|
||||||
cat $fifo2
|
cat $fifo2
|
||||||
exit "$(cat $fifo3)"
|
exit "$(cat $fifo3)"
|
||||||
|
|
||||||
|
163
doc/fzf.txt
163
doc/fzf.txt
@@ -1,17 +1,19 @@
|
|||||||
fzf.txt fzf Last change: November 23 2019
|
fzf.txt fzf Last change: April 4 2020
|
||||||
FZF - TABLE OF CONTENTS *fzf* *fzf-toc*
|
FZF - TABLE OF CONTENTS *fzf* *fzf-toc*
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
FZF Vim integration
|
FZF Vim integration
|
||||||
|
Installation
|
||||||
Summary
|
Summary
|
||||||
:FZF[!]
|
:FZF[!]
|
||||||
Configuration
|
Configuration
|
||||||
Examples
|
Examples
|
||||||
|
Explanation of g:fzf_colors
|
||||||
fzf#run
|
fzf#run
|
||||||
fzf#wrap
|
fzf#wrap
|
||||||
Tips
|
Tips
|
||||||
fzf inside terminal buffer
|
fzf inside terminal buffer
|
||||||
Starting fzf in Neovim floating window
|
Starting fzf in a popup window
|
||||||
Hide statusline
|
Hide statusline
|
||||||
License
|
License
|
||||||
|
|
||||||
@@ -19,6 +21,46 @@ FZF VIM INTEGRATION *fzf-vim-integration*
|
|||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
INSTALLATION *fzf-installation*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Once you have fzf installed, you can enable it inside Vim simply by adding the
|
||||||
|
directory to 'runtimepath' in your Vim configuration file. The path may differ
|
||||||
|
depending on the package manager.
|
||||||
|
>
|
||||||
|
" If installed using Homebrew
|
||||||
|
set rtp+=/usr/local/opt/fzf
|
||||||
|
|
||||||
|
" If installed using git
|
||||||
|
set rtp+=~/.fzf
|
||||||
|
<
|
||||||
|
If you use {vim-plug}{1}, the same can be written as:
|
||||||
|
>
|
||||||
|
" If installed using Homebrew
|
||||||
|
Plug '/usr/local/opt/fzf'
|
||||||
|
|
||||||
|
" If installed using git
|
||||||
|
Plug '~/.fzf'
|
||||||
|
<
|
||||||
|
But if you want the latest Vim plugin file from GitHub rather than the one
|
||||||
|
included in the package, write:
|
||||||
|
>
|
||||||
|
Plug 'junegunn/fzf'
|
||||||
|
<
|
||||||
|
The Vim plugin will pick up fzf binary available on the system. If fzf is not
|
||||||
|
found on `$PATH`, it will ask you if it should download the latest binary for
|
||||||
|
you.
|
||||||
|
|
||||||
|
To make sure that you have the latest version of the binary, set up
|
||||||
|
post-update hook like so:
|
||||||
|
|
||||||
|
*fzf#install*
|
||||||
|
>
|
||||||
|
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
|
||||||
|
<
|
||||||
|
{1} https://github.com/junegunn/vim-plug
|
||||||
|
|
||||||
|
|
||||||
SUMMARY *fzf-summary*
|
SUMMARY *fzf-summary*
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
@@ -38,12 +80,12 @@ the basic file selector command built on top of them.
|
|||||||
- Basic fuzzy file selector
|
- Basic fuzzy file selector
|
||||||
- A reference implementation for those who don't want to write VimScript to
|
- A reference implementation for those who don't want to write VimScript to
|
||||||
implement custom commands
|
implement custom commands
|
||||||
- If you're looking for more such commands, check out {fzf.vim}{1} project.
|
- If you're looking for more such commands, check out {fzf.vim}{2} project.
|
||||||
|
|
||||||
The most important of all is `fzf#run`, but it would be easier to understand
|
The most important of all is `fzf#run`, but it would be easier to understand
|
||||||
the whole if we start off with `:FZF` command.
|
the whole if we start off with `:FZF` command.
|
||||||
|
|
||||||
{1} https://github.com/junegunn/fzf.vim
|
{2} https://github.com/junegunn/fzf.vim
|
||||||
|
|
||||||
|
|
||||||
:FZF[!]
|
:FZF[!]
|
||||||
@@ -112,8 +154,9 @@ Examples~
|
|||||||
\ 'ctrl-v': 'vsplit' }
|
\ 'ctrl-v': 'vsplit' }
|
||||||
|
|
||||||
" Default fzf layout
|
" Default fzf layout
|
||||||
" - down / up / left / right
|
" - down / up / left / right / window
|
||||||
let g:fzf_layout = { 'down': '~40%' }
|
let g:fzf_layout = { 'down': '~40%' }
|
||||||
|
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||||
|
|
||||||
" You can set up fzf window using a Vim command (Neovim or latest Vim 8 required)
|
" You can set up fzf window using a Vim command (Neovim or latest Vim 8 required)
|
||||||
let g:fzf_layout = { 'window': 'enew' }
|
let g:fzf_layout = { 'window': 'enew' }
|
||||||
@@ -144,6 +187,51 @@ Examples~
|
|||||||
let g:fzf_history_dir = '~/.local/share/fzf-history'
|
let g:fzf_history_dir = '~/.local/share/fzf-history'
|
||||||
<
|
<
|
||||||
|
|
||||||
|
Explanation of g:fzf_colors~
|
||||||
|
*fzf-explanation-of-gfzfcolors*
|
||||||
|
|
||||||
|
`g:fzf_colors` is a dictionary mapping fzf elements to a color specification
|
||||||
|
list:
|
||||||
|
>
|
||||||
|
element: [ component, group1 [, group2, ...] ]
|
||||||
|
<
|
||||||
|
- `element` is an fzf element to apply a color to:
|
||||||
|
|
||||||
|
----------------------+------------------------------------------------------
|
||||||
|
Element | Description ~
|
||||||
|
----------------------+------------------------------------------------------
|
||||||
|
`fg` / `bg` / `hl` | Item (foreground / background / highlight)
|
||||||
|
`fg+` / `bg+` / `hl+` | Current item (foreground / background / highlight)
|
||||||
|
`hl` / `hl+` | Highlighted substrings (normal / current)
|
||||||
|
`gutter` | Background of the gutter on the left
|
||||||
|
`pointer` | Pointer to the current line ( `>` )
|
||||||
|
`marker` | Multi-select marker ( `>` )
|
||||||
|
`border` | Border around the window ( `--border` and `--preview` )
|
||||||
|
`header` | Header ( `--header` or `--header-lines` )
|
||||||
|
`info` | Info line (match counters)
|
||||||
|
`spinner` | Streaming input indicator
|
||||||
|
`prompt` | Prompt before query ( `> ` )
|
||||||
|
----------------------+------------------------------------------------------
|
||||||
|
- `component` specifies the component (`fg` / `bg`) from which to extract the
|
||||||
|
color when considering each of the following highlight groups
|
||||||
|
- `group1 [, group2, ...]` is a list of highlight groups that are searched (in
|
||||||
|
order) for a matching color definition
|
||||||
|
|
||||||
|
For example, consider the following specification:
|
||||||
|
>
|
||||||
|
'prompt': ['fg', 'Conditional', 'Comment'],
|
||||||
|
<
|
||||||
|
This means we color the prompt - using the `fg` attribute of the `Conditional`
|
||||||
|
if it exists, - otherwise use the `fg` attribute of the `Comment` highlight
|
||||||
|
group if it exists, - otherwise fall back to the default color settings for
|
||||||
|
the prompt.
|
||||||
|
|
||||||
|
You can examine the color option generated according the setting by printing
|
||||||
|
the result of `fzf#wrap()` function like so:
|
||||||
|
>
|
||||||
|
:echo fzf#wrap()
|
||||||
|
<
|
||||||
|
|
||||||
FZF#RUN
|
FZF#RUN
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
@@ -203,7 +291,9 @@ The following table summarizes the available options.
|
|||||||
`options` | string/list | Options to fzf
|
`options` | string/list | Options to fzf
|
||||||
`dir` | string | Working directory
|
`dir` | string | Working directory
|
||||||
`up` / `down` / `left` / `right` | number/string | (Layout) Window position and size (e.g. `20` , `50%` )
|
`up` / `down` / `left` / `right` | number/string | (Layout) Window position and size (e.g. `20` , `50%` )
|
||||||
|
`tmux` | string | (Layout) fzf-tmux options (e.g. `-p90%,60%` )
|
||||||
`window` (Vim 8 / Neovim) | string | (Layout) Command to open fzf window (e.g. `vertical aboveleft 30new` )
|
`window` (Vim 8 / Neovim) | string | (Layout) Command to open fzf window (e.g. `vertical aboveleft 30new` )
|
||||||
|
`window` (Vim 8 / Neovim) | dict | (Layout) Popup window settings (e.g. `{'width': 0.9, 'height': 0.6}` )
|
||||||
---------------------------+---------------+----------------------------------------------------------------------
|
---------------------------+---------------+----------------------------------------------------------------------
|
||||||
|
|
||||||
`options` entry can be either a string or a list. For simple cases, string
|
`options` entry can be either a string or a list. For simple cases, string
|
||||||
@@ -212,6 +302,19 @@ should suffice, but prefer to use list type to avoid escaping issues.
|
|||||||
call fzf#run({'options': '--reverse --prompt "C:\\Program Files\\"'})
|
call fzf#run({'options': '--reverse --prompt "C:\\Program Files\\"'})
|
||||||
call fzf#run({'options': ['--reverse', '--prompt', 'C:\Program Files\']})
|
call fzf#run({'options': ['--reverse', '--prompt', 'C:\Program Files\']})
|
||||||
<
|
<
|
||||||
|
When `window` entry is a dictionary, fzf will start in a popup window. The
|
||||||
|
following options are allowed:
|
||||||
|
|
||||||
|
- Required:
|
||||||
|
- `width` [float range [0 ~ 1]] or [integer range [8 ~ ]]
|
||||||
|
- `height` [float range [0 ~ 1]] or [integer range [4 ~ ]]
|
||||||
|
- Optional:
|
||||||
|
- `yoffset` [float default 0.5 range [0 ~ 1]]
|
||||||
|
- `xoffset` [float default 0.5 range [0 ~ 1]]
|
||||||
|
- `highlight` [string default `'Comment'`]: Highlight group for border
|
||||||
|
- `border` [string default `rounded`]: Border style
|
||||||
|
- `rounded` / `sharp` / `horizontal` / `vertical` / `top` / `bottom` / `left` / `right`
|
||||||
|
|
||||||
|
|
||||||
FZF#WRAP
|
FZF#WRAP
|
||||||
==============================================================================
|
==============================================================================
|
||||||
@@ -291,29 +394,30 @@ The latest versions of Vim and Neovim include builtin terminal emulator
|
|||||||
- `call fzf#run({'left': '30%'})` or `let g:fzf_layout = {'left': '30%'}`
|
- `call fzf#run({'left': '30%'})` or `let g:fzf_layout = {'left': '30%'}`
|
||||||
|
|
||||||
|
|
||||||
Starting fzf in Neovim floating window~
|
Starting fzf in a popup window~
|
||||||
*fzf-starting-fzf-in-neovim-floating-window*
|
*fzf-starting-fzf-in-a-popup-window*
|
||||||
>
|
>
|
||||||
" Using floating windows of Neovim to start fzf
|
" Required:
|
||||||
if has('nvim')
|
" - width [float range [0 ~ 1]]
|
||||||
let $FZF_DEFAULT_OPTS .= ' --border --margin=0,2'
|
" - height [float range [0 ~ 1]]
|
||||||
|
"
|
||||||
function! FloatingFZF()
|
" Optional:
|
||||||
let width = float2nr(&columns * 0.9)
|
" - xoffset [float default 0.5 range [0 ~ 1]]
|
||||||
let height = float2nr(&lines * 0.6)
|
" - yoffset [float default 0.5 range [0 ~ 1]]
|
||||||
let opts = { 'relative': 'editor',
|
" - highlight [string default 'Comment']: Highlight group for border
|
||||||
\ 'row': (&lines - height) / 2,
|
" - border [string default 'rounded']: Border style
|
||||||
\ 'col': (&columns - width) / 2,
|
" - 'rounded' / 'sharp' / 'horizontal' / 'vertical' / 'top' / 'bottom' / 'left' / 'right'
|
||||||
\ 'width': width,
|
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||||
\ 'height': height }
|
<
|
||||||
|
Alternatively, you can make fzf open in a tmux popup window (requires tmux 3.2
|
||||||
let win = nvim_open_win(nvim_create_buf(v:false, v:true), v:true, opts)
|
or above) by putting fzf-tmux options in `tmux` key.
|
||||||
call setwinvar(win, '&winhighlight', 'NormalFloat:Normal')
|
>
|
||||||
endfunction
|
" See `man fzf-tmux` for available options
|
||||||
|
if exists('$TMUX')
|
||||||
let g:fzf_layout = { 'window': 'call FloatingFZF()' }
|
let g:fzf_layout = { 'tmux': '-p90%,60%' }
|
||||||
|
else
|
||||||
|
let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
|
||||||
endif
|
endif
|
||||||
|
|
||||||
<
|
<
|
||||||
|
|
||||||
Hide statusline~
|
Hide statusline~
|
||||||
@@ -323,8 +427,9 @@ When fzf starts in a terminal buffer, the file type of the buffer is set to
|
|||||||
`fzf`. So you can set up `FileType fzf` autocmd to customize the settings of
|
`fzf`. So you can set up `FileType fzf` autocmd to customize the settings of
|
||||||
the window.
|
the window.
|
||||||
|
|
||||||
For example, if you use the default layout (`{'down': '~40%'}`) on Neovim, you
|
For example, if you use a non-popup layout (e.g. `{'down': '40%'}`) on
|
||||||
might want to temporarily disable the statusline for a cleaner look.
|
Neovim, you might want to temporarily disable the statusline for a cleaner
|
||||||
|
look.
|
||||||
>
|
>
|
||||||
if has('nvim') && !exists('g:fzf_layout')
|
if has('nvim') && !exists('g:fzf_layout')
|
||||||
autocmd! FileType fzf
|
autocmd! FileType fzf
|
||||||
@@ -338,7 +443,7 @@ LICENSE *fzf-license*
|
|||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2019 Junegunn Choi
|
Copyright (c) 2013-2020 Junegunn Choi
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
vim:tw=78:sw=2:ts=2:ft=help:norl:nowrap:
|
vim:tw=78:sw=2:ts=2:ft=help:norl:nowrap:
|
||||||
|
22
go.mod
22
go.mod
@@ -1,19 +1,15 @@
|
|||||||
module github.com/junegunn/fzf
|
module github.com/junegunn/fzf
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gdamore/encoding v0.0.0-20151215212835-b23993cbb635 // indirect
|
github.com/gdamore/tcell v1.4.0
|
||||||
github.com/gdamore/tcell v0.0.0-20170915061752-0a0db94084df
|
github.com/lucasb-eyer/go-colorful v1.0.3 // indirect
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
|
github.com/mattn/go-isatty v0.0.12
|
||||||
github.com/jtolds/gls v4.2.1+incompatible // indirect
|
github.com/mattn/go-runewidth v0.0.8
|
||||||
github.com/lucasb-eyer/go-colorful v0.0.0-20170223221042-c900de9dbbc7 // indirect
|
github.com/mattn/go-shellwords v1.0.9
|
||||||
github.com/mattn/go-isatty v0.0.0-20160806122752-66b8e73f3f5c
|
github.com/saracen/walker v0.0.0-20191201085201-324a081bae7e
|
||||||
github.com/mattn/go-runewidth v0.0.0-20170201023540-14207d285c6c
|
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d
|
||||||
github.com/mattn/go-shellwords v1.0.3
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect
|
golang.org/x/text v0.3.2 // indirect
|
||||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect
|
|
||||||
golang.org/x/crypto v0.0.0-20170728183002-558b6879de74
|
|
||||||
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 // indirect
|
|
||||||
golang.org/x/text v0.0.0-20170530162606-4ee4af566555 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.13
|
go 1.13
|
||||||
|
71
go.sum
71
go.sum
@@ -1,26 +1,45 @@
|
|||||||
github.com/gdamore/encoding v0.0.0-20151215212835-b23993cbb635 h1:hheUEMzaOie/wKeIc1WPa7CDVuIO5hqQxjS+dwTQEnI=
|
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
github.com/gdamore/encoding v0.0.0-20151215212835-b23993cbb635/go.mod h1:yrQYJKKDTrHmbYxI7CYi+/hbdiDT2m4Hj+t0ikCjsrQ=
|
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||||
github.com/gdamore/tcell v0.0.0-20170915061752-0a0db94084df h1:tLS1QD2puA1USLvkEnGfOt+Zp2ijtNIK3z2YFaIZZR4=
|
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||||
github.com/gdamore/tcell v0.0.0-20170915061752-0a0db94084df/go.mod h1:tqyG50u7+Ctv1w5VX67kLzKcj9YXR/JSBZQq/+mLl1A=
|
github.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
github.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4=
|
||||||
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
|
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||||
github.com/lucasb-eyer/go-colorful v0.0.0-20170223221042-c900de9dbbc7 h1:G52I+Gk/wPD4HKvKT0Vxxp9OUPxqKs3OK6rffSPtNkA=
|
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/lucasb-eyer/go-colorful v0.0.0-20170223221042-c900de9dbbc7/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.0-20160806122752-66b8e73f3f5c h1:3nKFouDdpgGUV/uerJcYWH45ZbJzX0SiVWfTgmUeTzc=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.0-20160806122752-66b8e73f3f5c/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||||
github.com/mattn/go-runewidth v0.0.0-20170201023540-14207d285c6c h1:eFzthqtg3W6Pihj3DMTXLAF4f+ge5r5Ie5g6HLIZAF0=
|
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.0-20170201023540-14207d285c6c/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-shellwords v1.0.3 h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk=
|
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
|
||||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/mattn/go-shellwords v1.0.9 h1:eaB5JspOwiKKcHdqcjbfe5lA9cNn/4NRRtddXJCimqk=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
|
github.com/saracen/walker v0.0.0-20191201085201-324a081bae7e h1:NO86zOn5ScSKW8wRbMaSIcjDZUFpWdCQQnexRqZ9h9A=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
github.com/saracen/walker v0.0.0-20191201085201-324a081bae7e/go.mod h1:G0Z6yVPru183i2MuRJx1DcR4dgIZtLcTdaaE/pC1BJU=
|
||||||
golang.org/x/crypto v0.0.0-20170728183002-558b6879de74 h1:/0jf0Cx3u07Ma4EzUA6NIGuvk9hb3Br6i9V8atthkwk=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
golang.org/x/crypto v0.0.0-20170728183002-558b6879de74/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 h1:rM0ROo5vb9AdYJi1110yjWGMej9ITfKddS89P3Fkhug=
|
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U=
|
||||||
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/text v0.0.0-20170530162606-4ee4af566555 h1:pjwO9HxObpgZBurDvTLFbSinaf3+cKpTAjVfiGaHwrI=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/text v0.0.0-20170530162606-4ee4af566555/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
|
||||||
|
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191011211836-4c025a95b26e h1:1o2bDs9pCd2xFhdwqJTrCIswAeEsn4h/PCNelWpfcsI=
|
||||||
|
golang.org/x/tools v0.0.0-20191011211836-4c025a95b26e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
20
install
20
install
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
version=0.20.0
|
version=0.23.1
|
||||||
auto_completion=
|
auto_completion=
|
||||||
key_bindings=
|
key_bindings=
|
||||||
update_config=2
|
update_config=2
|
||||||
@@ -28,9 +28,6 @@ usage: $0 [OPTIONS]
|
|||||||
--no-bash Do not set up bash configuration
|
--no-bash Do not set up bash configuration
|
||||||
--no-zsh Do not set up zsh configuration
|
--no-zsh Do not set up zsh configuration
|
||||||
--no-fish Do not set up fish configuration
|
--no-fish Do not set up fish configuration
|
||||||
|
|
||||||
--32 Download 32-bit binary
|
|
||||||
--64 Download 64-bit binary
|
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,8 +53,6 @@ for opt in "$@"; do
|
|||||||
--no-completion) auto_completion=0 ;;
|
--no-completion) auto_completion=0 ;;
|
||||||
--update-rc) update_config=1 ;;
|
--update-rc) update_config=1 ;;
|
||||||
--no-update-rc) update_config=0 ;;
|
--no-update-rc) update_config=0 ;;
|
||||||
--32) binary_arch=386 ;;
|
|
||||||
--64) binary_arch=amd64 ;;
|
|
||||||
--bin) ;;
|
--bin) ;;
|
||||||
--no-bash) shells=${shells/bash/} ;;
|
--no-bash) shells=${shells/bash/} ;;
|
||||||
--no-zsh) shells=${shells/zsh/} ;;
|
--no-zsh) shells=${shells/zsh/} ;;
|
||||||
@@ -179,24 +174,17 @@ binary_available=1
|
|||||||
binary_error=""
|
binary_error=""
|
||||||
case "$archi" in
|
case "$archi" in
|
||||||
Darwin\ *64) download fzf-$version-darwin_${binary_arch:-amd64}.tgz ;;
|
Darwin\ *64) download fzf-$version-darwin_${binary_arch:-amd64}.tgz ;;
|
||||||
Darwin\ *86) download fzf-$version-darwin_${binary_arch:-386}.tgz ;;
|
|
||||||
Linux\ armv5*) download fzf-$version-linux_${binary_arch:-arm5}.tgz ;;
|
Linux\ armv5*) download fzf-$version-linux_${binary_arch:-arm5}.tgz ;;
|
||||||
Linux\ armv6*) download fzf-$version-linux_${binary_arch:-arm6}.tgz ;;
|
Linux\ armv6*) download fzf-$version-linux_${binary_arch:-arm6}.tgz ;;
|
||||||
Linux\ armv7*) download fzf-$version-linux_${binary_arch:-arm7}.tgz ;;
|
Linux\ armv7*) download fzf-$version-linux_${binary_arch:-arm7}.tgz ;;
|
||||||
Linux\ armv8*) download fzf-$version-linux_${binary_arch:-arm8}.tgz ;;
|
Linux\ armv8*) download fzf-$version-linux_${binary_arch:-arm8}.tgz ;;
|
||||||
Linux\ aarch64*) download fzf-$version-linux_${binary_arch:-arm8}.tgz ;;
|
Linux\ aarch64*) download fzf-$version-linux_${binary_arch:-arm8}.tgz ;;
|
||||||
Linux\ *64) download fzf-$version-linux_${binary_arch:-amd64}.tgz ;;
|
Linux\ *64) download fzf-$version-linux_${binary_arch:-amd64}.tgz ;;
|
||||||
Linux\ *86) download fzf-$version-linux_${binary_arch:-386}.tgz ;;
|
|
||||||
FreeBSD\ *64) download fzf-$version-freebsd_${binary_arch:-amd64}.tgz ;;
|
FreeBSD\ *64) download fzf-$version-freebsd_${binary_arch:-amd64}.tgz ;;
|
||||||
FreeBSD\ *86) download fzf-$version-freebsd_${binary_arch:-386}.tgz ;;
|
|
||||||
OpenBSD\ *64) download fzf-$version-openbsd_${binary_arch:-amd64}.tgz ;;
|
OpenBSD\ *64) download fzf-$version-openbsd_${binary_arch:-amd64}.tgz ;;
|
||||||
OpenBSD\ *86) download fzf-$version-openbsd_${binary_arch:-386}.tgz ;;
|
|
||||||
CYGWIN*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
CYGWIN*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
||||||
MINGW*\ *86) download fzf-$version-windows_${binary_arch:-386}.zip ;;
|
|
||||||
MINGW*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
MINGW*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
||||||
MSYS*\ *86) download fzf-$version-windows_${binary_arch:-386}.zip ;;
|
|
||||||
MSYS*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
MSYS*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
||||||
Windows*\ *86) download fzf-$version-windows_${binary_arch:-386}.zip ;;
|
|
||||||
Windows*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
Windows*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
||||||
*) binary_available=0 binary_error=1 ;;
|
*) binary_available=0 binary_error=1 ;;
|
||||||
esac
|
esac
|
||||||
@@ -384,7 +372,11 @@ fi
|
|||||||
|
|
||||||
if [ $update_config -eq 1 ]; then
|
if [ $update_config -eq 1 ]; then
|
||||||
echo 'Finished. Restart your shell or reload config file.'
|
echo 'Finished. Restart your shell or reload config file.'
|
||||||
[[ "$shells" =~ bash ]] && echo ' source ~/.bashrc # bash'
|
if [[ "$shells" =~ bash ]]; then
|
||||||
|
echo -n ' source ~/.bashrc # bash'
|
||||||
|
[[ "$archi" =~ Darwin ]] && echo -n ' (.bashrc should be loaded from .bash_profile)'
|
||||||
|
echo
|
||||||
|
fi
|
||||||
[[ "$shells" =~ zsh ]] && echo " source ${ZDOTDIR:-~}/.zshrc # zsh"
|
[[ "$shells" =~ zsh ]] && echo " source ${ZDOTDIR:-~}/.zshrc # zsh"
|
||||||
[[ "$shells" =~ fish ]] && [ $key_bindings -eq 1 ] && echo ' fzf_key_bindings # fish'
|
[[ "$shells" =~ fish ]] && [ $key_bindings -eq 1 ] && echo ' fzf_key_bindings # fish'
|
||||||
echo
|
echo
|
||||||
|
73
install.ps1
Normal file
73
install.ps1
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
$version="0.23.1"
|
||||||
|
|
||||||
|
if ([Environment]::Is64BitProcess) {
|
||||||
|
$binary_arch="amd64"
|
||||||
|
} else {
|
||||||
|
$binary_arch="386"
|
||||||
|
}
|
||||||
|
|
||||||
|
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||||
|
|
||||||
|
function check_binary () {
|
||||||
|
Write-Host " - Checking fzf executable ... " -NoNewline
|
||||||
|
$output=cmd /c $fzf_base\bin\fzf.exe --version 2>&1
|
||||||
|
if (-not $?) {
|
||||||
|
Write-Host "Error: $output"
|
||||||
|
$binary_error="Invalid binary"
|
||||||
|
} else {
|
||||||
|
$output=(-Split $output)[0]
|
||||||
|
if ($version -ne $output) {
|
||||||
|
Write-Host "$output != $version"
|
||||||
|
$binary_error="Invalid version"
|
||||||
|
} else {
|
||||||
|
Write-Host "$output"
|
||||||
|
$binary_error=""
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Remove-Item "$fzf_base\bin\fzf.exe"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function download {
|
||||||
|
param($file)
|
||||||
|
Write-Host "Downloading bin/fzf ..."
|
||||||
|
if ("$version" -ne "alpha") {
|
||||||
|
if (Test-Path "$fzf_base\bin\fzf.exe") {
|
||||||
|
Write-Host " - Already exists"
|
||||||
|
if (check_binary) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not (Test-Path "$fzf_base\bin")) {
|
||||||
|
md "$fzf_base\bin"
|
||||||
|
}
|
||||||
|
if (-not $?) {
|
||||||
|
$binary_error="Failed to create bin directory"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cd "$fzf_base\bin"
|
||||||
|
if ("$version" -eq "alpha") {
|
||||||
|
$url="https://github.com/junegunn/fzf-bin/releases/download/alpha/$file"
|
||||||
|
} else {
|
||||||
|
$url="https://github.com/junegunn/fzf-bin/releases/download/$version/$file"
|
||||||
|
}
|
||||||
|
$temp=$env:TMP + "\fzf.zip"
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
|
(New-Object Net.WebClient).DownloadFile($url, $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$temp"))
|
||||||
|
if ($?) {
|
||||||
|
(Microsoft.PowerShell.Archive\Expand-Archive -Path $temp -DestinationPath .); (Remove-Item $temp)
|
||||||
|
} else {
|
||||||
|
$binary_error="Failed to download with powershell"
|
||||||
|
}
|
||||||
|
if (-not (Test-Path fzf.exe)) {
|
||||||
|
$binary_error="Failed to download $file"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check_binary >$null
|
||||||
|
}
|
||||||
|
|
||||||
|
download "fzf-$version-windows_$binary_arch.zip"
|
||||||
|
|
||||||
|
Write-Host 'For more information, see: https://github.com/junegunn/fzf'
|
6
main.go
6
main.go
@@ -1,9 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/junegunn/fzf/src"
|
import (
|
||||||
|
"github.com/junegunn/fzf/src"
|
||||||
|
"github.com/junegunn/fzf/src/protector"
|
||||||
|
)
|
||||||
|
|
||||||
var revision string
|
var revision string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
protector.Protect()
|
||||||
fzf.Run(fzf.ParseOptions(), revision)
|
fzf.Run(fzf.ParseOptions(), revision)
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
.ig
|
.ig
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2019 Junegunn Choi
|
Copyright (c) 2013-2020 Junegunn Choi
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -21,25 +21,39 @@ 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 "Dec 2019" "fzf 0.20.0" "fzf-tmux - open fzf in tmux split pane"
|
.TH fzf-tmux 1 "Oct 2020" "fzf 0.23.1" "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
|
||||||
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B fzf-tmux [-u|-d [HEIGHT[%]]] [-l|-r [WIDTH[%]]] [--] [FZF OPTIONS]
|
.B fzf-tmux [LAYOUT OPTIONS] [--] [FZF OPTIONS]
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
fzf-tmux is a wrapper script for fzf that opens fzf in a tmux split pane. It is
|
fzf-tmux is a wrapper script for fzf that opens fzf in a tmux split pane or in
|
||||||
designed to work just like fzf except that it does not take up the whole
|
a tmux popup window. It is designed to work just like fzf except that it does
|
||||||
screen. You can safely use fzf-tmux instead of fzf in your scripts as the extra
|
not take up the whole screen. You can safely use fzf-tmux instead of fzf in
|
||||||
options will be silently ignored if you're not on tmux.
|
your scripts as the extra options will be silently ignored if you're not on
|
||||||
|
tmux.
|
||||||
|
|
||||||
.SH OPTIONS
|
.SH LAYOUT OPTIONS
|
||||||
.SS Layout
|
|
||||||
|
|
||||||
(default: \fB-d 50%\fR)
|
(default layout: \fB-d 50%\fR)
|
||||||
|
|
||||||
|
.SS Popup window
|
||||||
|
(requires tmux 3.2 or above)
|
||||||
|
.TP
|
||||||
|
.B "-p [WIDTH[%][,HEIGHT[%]]]"
|
||||||
|
.TP
|
||||||
|
.B "-w WIDTH[%]"
|
||||||
|
.TP
|
||||||
|
.B "-h WIDTH[%]"
|
||||||
|
.TP
|
||||||
|
.B "-x COL"
|
||||||
|
.TP
|
||||||
|
.B "-y ROW"
|
||||||
|
|
||||||
|
.SS Split pane
|
||||||
.TP
|
.TP
|
||||||
.B "-u [height[%]]"
|
.B "-u [height[%]]"
|
||||||
Split above (up)
|
Split above (up)
|
||||||
|
122
man/man1/fzf.1
122
man/man1/fzf.1
@@ -1,7 +1,7 @@
|
|||||||
.ig
|
.ig
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2019 Junegunn Choi
|
Copyright (c) 2013-2020 Junegunn Choi
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -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 "Dec 2019" "fzf 0.20.0" "fzf - a command-line fuzzy finder"
|
.TH fzf 1 "Oct 2020" "fzf 0.23.1" "fzf - a command-line fuzzy finder"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
fzf - a command-line fuzzy finder
|
fzf - a command-line fuzzy finder
|
||||||
@@ -130,6 +130,10 @@ the details.
|
|||||||
.B "--cycle"
|
.B "--cycle"
|
||||||
Enable cyclic scroll
|
Enable cyclic scroll
|
||||||
.TP
|
.TP
|
||||||
|
.B "--keep-right"
|
||||||
|
Keep the right end of the line visible when it's too long. Effective only when
|
||||||
|
the query string is empty.
|
||||||
|
.TP
|
||||||
.B "--no-hscroll"
|
.B "--no-hscroll"
|
||||||
Disable horizontal scroll
|
Disable horizontal scroll
|
||||||
.TP
|
.TP
|
||||||
@@ -178,8 +182,16 @@ Choose the layout (default: default)
|
|||||||
A synonym for \fB--layout=reverse\fB
|
A synonym for \fB--layout=reverse\fB
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B "--border"
|
.BI "--border" [=STYLE]
|
||||||
Draw border above and below the finder
|
Draw border around the finder
|
||||||
|
|
||||||
|
.br
|
||||||
|
.BR rounded " Border with rounded corners (default)"
|
||||||
|
.br
|
||||||
|
.BR sharp " Border with sharp corners"
|
||||||
|
.br
|
||||||
|
.BR horizontal " Horizontal lines above and below the finder"
|
||||||
|
.br
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B "--no-unicode"
|
.B "--no-unicode"
|
||||||
@@ -231,6 +243,12 @@ A synonym for \fB--info=hidden\fB
|
|||||||
.BI "--prompt=" "STR"
|
.BI "--prompt=" "STR"
|
||||||
Input prompt (default: '> ')
|
Input prompt (default: '> ')
|
||||||
.TP
|
.TP
|
||||||
|
.BI "--pointer=" "STR"
|
||||||
|
Pointer to the current line (default: '>')
|
||||||
|
.TP
|
||||||
|
.BI "--marker=" "STR"
|
||||||
|
Multi-select marker (default: '>')
|
||||||
|
.TP
|
||||||
.BI "--header=" "STR"
|
.BI "--header=" "STR"
|
||||||
The given string will be printed as the sticky header. The lines are displayed
|
The given string will be printed as the sticky header. The lines are displayed
|
||||||
in the given order from top to bottom regardless of \fB--layout\fR option, and
|
in the given order from top to bottom regardless of \fB--layout\fR option, and
|
||||||
@@ -274,8 +292,8 @@ format.
|
|||||||
\fBbg+ \fRBackground (current line)
|
\fBbg+ \fRBackground (current line)
|
||||||
\fBgutter \fRGutter on the left (defaults to \fBbg+\fR)
|
\fBgutter \fRGutter on the left (defaults to \fBbg+\fR)
|
||||||
\fBhl+ \fRHighlighted substrings (current line)
|
\fBhl+ \fRHighlighted substrings (current line)
|
||||||
\fBinfo \fRInfo
|
\fBinfo \fRInfo line (match counters)
|
||||||
\fBborder \fRBorder of the preview window and horizontal separators (\fB--border\fR)
|
\fBborder \fRBorder around the window (\fB--border\fR and \fB--preview\fR)
|
||||||
\fBprompt \fRPrompt
|
\fBprompt \fRPrompt
|
||||||
\fBpointer \fRPointer to the current line
|
\fBpointer \fRPointer to the current line
|
||||||
\fBmarker \fRMulti-select marker
|
\fBmarker \fRMulti-select marker
|
||||||
@@ -363,14 +381,7 @@ Preview window will be updated even when there is no match for the current
|
|||||||
query if any of the placeholder expressions evaluates to a non-empty string.
|
query if any of the placeholder expressions evaluates to a non-empty string.
|
||||||
.RE
|
.RE
|
||||||
.TP
|
.TP
|
||||||
.BI "--preview-window=" "[POSITION][:SIZE[%]][:noborder][:wrap][:hidden]"
|
.BI "--preview-window=" "[POSITION][:SIZE[%]][:rounded|sharp|noborder][:[no]wrap][:[no]cycle][:[no]hidden][:+SCROLL[-OFFSET]][:default]"
|
||||||
Determines the layout of the preview window. If the argument contains
|
|
||||||
\fB:hidden\fR, the preview window will be hidden by default until
|
|
||||||
\fBtoggle-preview\fR action is triggered. Long lines are truncated by default.
|
|
||||||
Line wrap can be enabled with \fB:wrap\fR flag.
|
|
||||||
|
|
||||||
If size is given as 0, preview window will not be visible, but fzf will still
|
|
||||||
execute the command in the background.
|
|
||||||
|
|
||||||
.RS
|
.RS
|
||||||
.B POSITION: (default: right)
|
.B POSITION: (default: right)
|
||||||
@@ -380,11 +391,47 @@ execute the command in the background.
|
|||||||
\fBright
|
\fBright
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
|
\fRDetermines the layout of the preview window. If the argument contains
|
||||||
|
\fB:hidden\fR, the preview window will be hidden by default until
|
||||||
|
\fBtoggle-preview\fR action is triggered. Long lines are truncated by default.
|
||||||
|
Line wrap can be enabled with \fB:wrap\fR flag. Cyclic scrolling is enabled
|
||||||
|
with \fB:cycle\fR flag.
|
||||||
|
|
||||||
|
If size is given as 0, preview window will not be visible, but fzf will still
|
||||||
|
execute the command in the background.
|
||||||
|
|
||||||
|
To change the style of the border of the preview window, specify one of
|
||||||
|
\fBrounded\fR (border with rounded edges, default), \fBsharp\fR (border with
|
||||||
|
sharp edges), or \fBnoborder\fR (no border).
|
||||||
|
|
||||||
|
\fB+SCROLL[-OFFSET]\fR determines the initial scroll offset of the preview
|
||||||
|
window. \fBSCROLL\fR can be either a numeric integer or a single-field index
|
||||||
|
expression that refers to a numeric integer. The optional \fB-OFFSET\fR part is
|
||||||
|
for adjusting the base offset so that you can see the text above it. It should
|
||||||
|
be given as a numeric integer (\fB-INTEGER\fR), or as a denominator form
|
||||||
|
(\fB-/INTEGER\fR) for specifying a fraction of the preview window height.
|
||||||
|
|
||||||
|
\fBdefault\fR resets all options previously set to the default.
|
||||||
|
|
||||||
.RS
|
.RS
|
||||||
e.g.
|
e.g.
|
||||||
\fBfzf --preview="head {}" --preview-window=up:30%
|
\fB# Non-default scroll window positions and sizes
|
||||||
fzf --preview="file {}" --preview-window=down:1\fR
|
fzf --preview="head {}" --preview-window=up:30%
|
||||||
|
fzf --preview="file {}" --preview-window=down:1
|
||||||
|
|
||||||
|
# Initial scroll offset is set to the line number of each line of
|
||||||
|
# git grep output *minus* 5 lines (-5)
|
||||||
|
git grep --line-number '' |
|
||||||
|
fzf --delimiter : --preview 'nl {1}' --preview-window +{2}-5
|
||||||
|
|
||||||
|
# Preview with bat, matching line in the middle of the window (-/2)
|
||||||
|
git grep --line-number '' |
|
||||||
|
fzf --delimiter : \\
|
||||||
|
--preview 'bat --style=numbers --color=always --highlight-line {2} {1}' \\
|
||||||
|
--preview-window +{2}-/2\fR
|
||||||
|
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
.SS Scripting
|
.SS Scripting
|
||||||
.TP
|
.TP
|
||||||
.BI "-q, --query=" "STR"
|
.BI "-q, --query=" "STR"
|
||||||
@@ -594,6 +641,8 @@ e.g.
|
|||||||
.br
|
.br
|
||||||
\fIend\fR
|
\fIend\fR
|
||||||
.br
|
.br
|
||||||
|
\fIinsert\fR
|
||||||
|
.br
|
||||||
\fIpgup\fR (\fIpage-up\fR)
|
\fIpgup\fR (\fIpage-up\fR)
|
||||||
.br
|
.br
|
||||||
\fIpgdn\fR (\fIpage-down\fR)
|
\fIpgdn\fR (\fIpage-down\fR)
|
||||||
@@ -615,12 +664,23 @@ e.g.
|
|||||||
or any single character
|
or any single character
|
||||||
|
|
||||||
.SS AVAILABLE EVENTS:
|
.SS AVAILABLE EVENTS:
|
||||||
\fIchange\fR (triggered whenever the query string is changed)
|
\fIchange\fR
|
||||||
.br
|
.RS
|
||||||
|
Triggered whenever the query string is changed
|
||||||
|
|
||||||
e.g.
|
e.g.
|
||||||
\fB# Moves cursor to the top (or bottom depending on --layout) whenever the query is changed
|
\fB# Moves cursor to the top (or bottom depending on --layout) whenever the query is changed
|
||||||
fzf --bind change:top\fR
|
fzf --bind change:top\fR
|
||||||
|
.RE
|
||||||
|
|
||||||
|
\fIbackward-eof\fR
|
||||||
|
.RS
|
||||||
|
Triggered when the query string is already empty and you try to delete it
|
||||||
|
backward.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
\fBfzf --bind backward-eof: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.
|
||||||
@@ -631,6 +691,7 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBaccept-non-empty\fR (same as \fBaccept\fR except that it prevents fzf from exiting without selection)
|
\fBaccept-non-empty\fR (same as \fBaccept\fR except that it prevents fzf from exiting without selection)
|
||||||
\fBbackward-char\fR \fIctrl-b left\fR
|
\fBbackward-char\fR \fIctrl-b left\fR
|
||||||
\fBbackward-delete-char\fR \fIctrl-h bspace\fR
|
\fBbackward-delete-char\fR \fIctrl-h bspace\fR
|
||||||
|
\fBbackward-delete-char/eof\fR (same as \fBbackward-delete-char\fR except aborts fzf if query is empty)
|
||||||
\fBbackward-kill-word\fR \fIalt-bs\fR
|
\fBbackward-kill-word\fR \fIalt-bs\fR
|
||||||
\fBbackward-word\fR \fIalt-b shift-left\fR
|
\fBbackward-word\fR \fIalt-b shift-left\fR
|
||||||
\fBbeginning-of-line\fR \fIctrl-a home\fR
|
\fBbeginning-of-line\fR \fIctrl-a home\fR
|
||||||
@@ -639,13 +700,12 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBclear-selection\fR (clear multi-selection)
|
\fBclear-selection\fR (clear multi-selection)
|
||||||
\fBclear-query\fR (clear query string)
|
\fBclear-query\fR (clear query string)
|
||||||
\fBdelete-char\fR \fIdel\fR
|
\fBdelete-char\fR \fIdel\fR
|
||||||
\fBdelete-char/eof\fR \fIctrl-d\fR
|
\fBdelete-char/eof\fR \fIctrl-d\fR (same as \fBdelete-char\fR except aborts fzf if query is empty)
|
||||||
\fBdeselect-all\fR (deselect all matches)
|
\fBdeselect-all\fR (deselect all matches)
|
||||||
\fBdown\fR \fIctrl-j ctrl-n down\fR
|
\fBdown\fR \fIctrl-j ctrl-n down\fR
|
||||||
\fBend-of-line\fR \fIctrl-e end\fR
|
\fBend-of-line\fR \fIctrl-e end\fR
|
||||||
\fBexecute(...)\fR (see below for the details)
|
\fBexecute(...)\fR (see below for the details)
|
||||||
\fBexecute-silent(...)\fR (see below for the details)
|
\fBexecute-silent(...)\fR (see below for the details)
|
||||||
\fRexecute-multi(...)\fR (deprecated in favor of \fB{+}\fR expression)
|
|
||||||
\fBforward-char\fR \fIctrl-f right\fR
|
\fBforward-char\fR \fIctrl-f right\fR
|
||||||
\fBforward-word\fR \fIalt-f shift-right\fR
|
\fBforward-word\fR \fIalt-f shift-right\fR
|
||||||
\fBignore\fR
|
\fBignore\fR
|
||||||
@@ -658,12 +718,16 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBpage-up\fR \fIpgup\fR
|
\fBpage-up\fR \fIpgup\fR
|
||||||
\fBhalf-page-down\fR
|
\fBhalf-page-down\fR
|
||||||
\fBhalf-page-up\fR
|
\fBhalf-page-up\fR
|
||||||
|
\fBpreview(...)\fR (see below for the details)
|
||||||
\fBpreview-down\fR \fIshift-down\fR
|
\fBpreview-down\fR \fIshift-down\fR
|
||||||
\fBpreview-up\fR \fIshift-up\fR
|
\fBpreview-up\fR \fIshift-up\fR
|
||||||
\fBpreview-page-down\fR
|
\fBpreview-page-down\fR
|
||||||
\fBpreview-page-up\fR
|
\fBpreview-page-up\fR
|
||||||
|
\fBpreview-half-page-down\fR
|
||||||
|
\fBpreview-half-page-up\fR
|
||||||
\fBprevious-history\fR (\fIctrl-p\fR on \fB--history\fR)
|
\fBprevious-history\fR (\fIctrl-p\fR on \fB--history\fR)
|
||||||
\fBprint-query\fR (print query and exit)
|
\fBprint-query\fR (print query and exit)
|
||||||
|
\fBrefresh-preview\fR
|
||||||
\fBreload(...)\fR (see below for the details)
|
\fBreload(...)\fR (see below for the details)
|
||||||
\fBreplace-query\fR (replace query string with the current selection)
|
\fBreplace-query\fR (replace query string with the current selection)
|
||||||
\fBselect-all\fR (select all matches)
|
\fBselect-all\fR (select all matches)
|
||||||
@@ -750,6 +814,24 @@ e.g.
|
|||||||
fzf --bind "change:reload:$RG_PREFIX {q} || true" \\
|
fzf --bind "change:reload:$RG_PREFIX {q} || true" \\
|
||||||
--ansi --phony --query "$INITIAL_QUERY"\fR
|
--ansi --phony --query "$INITIAL_QUERY"\fR
|
||||||
|
|
||||||
|
.SS PREVIEW BINDING
|
||||||
|
|
||||||
|
With \fBpreview(...)\fR action, you can specify multiple different preview
|
||||||
|
commands in addition to the default preview command given by \fB--preview\fR
|
||||||
|
option.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
# Default preview command with an extra preview binding
|
||||||
|
fzf --preview 'file {}' --bind '?:preview:cat {}'
|
||||||
|
|
||||||
|
# A preview binding with no default preview command
|
||||||
|
# (Preview window is initially empty)
|
||||||
|
fzf --bind '?:preview:cat {}'
|
||||||
|
|
||||||
|
# Preview window hidden by default, it appears when you first hit '?'
|
||||||
|
fzf --bind '?:preview:cat {}' --preview-window hidden
|
||||||
|
|
||||||
.SH AUTHOR
|
.SH AUTHOR
|
||||||
Junegunn Choi (\fIjunegunn.c@gmail.com\fR)
|
Junegunn Choi (\fIjunegunn.c@gmail.com\fR)
|
||||||
|
|
||||||
|
261
plugin/fzf.vim
261
plugin/fzf.vim
@@ -49,8 +49,13 @@ if s:is_win
|
|||||||
|
|
||||||
" Use utf-8 for fzf.vim commands
|
" Use utf-8 for fzf.vim commands
|
||||||
" Return array of shell commands for cmd.exe
|
" Return array of shell commands for cmd.exe
|
||||||
let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0)
|
|
||||||
function! s:enc_to_cp(str)
|
function! s:enc_to_cp(str)
|
||||||
|
if !has('iconv')
|
||||||
|
return a:str
|
||||||
|
endif
|
||||||
|
if !exists('s:codepage')
|
||||||
|
let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0)
|
||||||
|
endif
|
||||||
return iconv(a:str, &encoding, 'cp'.s:codepage)
|
return iconv(a:str, &encoding, 'cp'.s:codepage)
|
||||||
endfunction
|
endfunction
|
||||||
function! s:wrap_cmds(cmds)
|
function! s:wrap_cmds(cmds)
|
||||||
@@ -110,45 +115,65 @@ function! s:fzf_tempname()
|
|||||||
return s:fzf_call('tempname')
|
return s:fzf_call('tempname')
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
let s:default_layout = { 'down': '~40%' }
|
let s:layout_keys = ['window', 'tmux', 'up', 'down', 'left', 'right']
|
||||||
let s:layout_keys = ['window', 'up', 'down', 'left', 'right']
|
|
||||||
let s:fzf_go = s:base_dir.'/bin/fzf'
|
let s:fzf_go = s:base_dir.'/bin/fzf'
|
||||||
let s:fzf_tmux = s:base_dir.'/bin/fzf-tmux'
|
let s:fzf_tmux = s:base_dir.'/bin/fzf-tmux'
|
||||||
let s:install = s:base_dir.'/install'
|
|
||||||
let s:installed = 0
|
|
||||||
|
|
||||||
let s:cpo_save = &cpo
|
let s:cpo_save = &cpo
|
||||||
set cpo&vim
|
set cpo&vim
|
||||||
|
|
||||||
function! s:fzf_exec()
|
function! s:popup_support()
|
||||||
|
return has('nvim') ? has('nvim-0.4') : has('popupwin') && has('patch-8.2.191')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:default_layout()
|
||||||
|
return s:popup_support()
|
||||||
|
\ ? { 'window' : { 'width': 0.9, 'height': 0.6, 'highlight': 'Normal' } }
|
||||||
|
\ : { 'down': '~40%' }
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! fzf#install()
|
||||||
|
if s:is_win && !has('win32unix')
|
||||||
|
let script = s:base_dir.'/install.ps1'
|
||||||
|
if !filereadable(script)
|
||||||
|
throw script.' not found'
|
||||||
|
endif
|
||||||
|
let script = 'powershell -ExecutionPolicy Bypass -file ' . script
|
||||||
|
else
|
||||||
|
let script = s:base_dir.'/install'
|
||||||
|
if !executable(script)
|
||||||
|
throw script.' not found'
|
||||||
|
endif
|
||||||
|
let script .= ' --bin'
|
||||||
|
endif
|
||||||
|
|
||||||
|
call s:warn('Running fzf installer ...')
|
||||||
|
call system(script)
|
||||||
|
if v:shell_error
|
||||||
|
throw 'Failed to download fzf: '.script
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! fzf#exec()
|
||||||
if !exists('s:exec')
|
if !exists('s:exec')
|
||||||
if executable(s:fzf_go)
|
if executable(s:fzf_go)
|
||||||
let s:exec = s:fzf_go
|
let s:exec = s:fzf_go
|
||||||
elseif executable('fzf')
|
elseif executable('fzf')
|
||||||
let s:exec = 'fzf'
|
let s:exec = 'fzf'
|
||||||
elseif s:is_win && !has('win32unix')
|
elseif input('fzf executable not found. Download binary? (y/n) ') =~? '^y'
|
||||||
call s:warn('fzf executable not found.')
|
|
||||||
call s:warn('Download fzf binary for Windows from https://github.com/junegunn/fzf-bin/releases/')
|
|
||||||
call s:warn('and place it as '.s:base_dir.'\bin\fzf.exe')
|
|
||||||
throw 'fzf executable not found'
|
|
||||||
elseif !s:installed && executable(s:install) &&
|
|
||||||
\ input('fzf executable not found. Download binary? (y/n) ') =~? '^y'
|
|
||||||
redraw
|
redraw
|
||||||
echo
|
call fzf#install()
|
||||||
call s:warn('Downloading fzf binary. Please wait ...')
|
return fzf#exec()
|
||||||
let s:installed = 1
|
|
||||||
call system(s:install.' --bin')
|
|
||||||
return s:fzf_exec()
|
|
||||||
else
|
else
|
||||||
redraw
|
redraw
|
||||||
throw 'fzf executable not found'
|
throw 'fzf executable not found'
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
return fzf#shellescape(s:exec)
|
return s:exec
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:tmux_enabled()
|
function! s:tmux_enabled()
|
||||||
if has('gui_running')
|
if has('gui_running') || !exists('$TMUX')
|
||||||
return 0
|
return 0
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -157,10 +182,16 @@ function! s:tmux_enabled()
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
let s:tmux = 0
|
let s:tmux = 0
|
||||||
if exists('$TMUX') && executable(s:fzf_tmux)
|
if !executable(s:fzf_tmux)
|
||||||
|
if executable('fzf-tmux')
|
||||||
|
let s:fzf_tmux = 'fzf-tmux'
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
let output = system('tmux -V')
|
let output = system('tmux -V')
|
||||||
let s:tmux = !v:shell_error && output >= 'tmux 1.7'
|
let s:tmux = !v:shell_error && output >= 'tmux 1.7'
|
||||||
endif
|
|
||||||
return s:tmux
|
return s:tmux
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
@@ -169,21 +200,6 @@ function! s:escape(path)
|
|||||||
return s:is_win ? escape(path, '$') : path
|
return s:is_win ? escape(path, '$') : path
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Upgrade legacy options
|
|
||||||
function! s:upgrade(dict)
|
|
||||||
let copy = copy(a:dict)
|
|
||||||
if has_key(copy, 'tmux')
|
|
||||||
let copy.down = remove(copy, 'tmux')
|
|
||||||
endif
|
|
||||||
if has_key(copy, 'tmux_height')
|
|
||||||
let copy.down = remove(copy, 'tmux_height')
|
|
||||||
endif
|
|
||||||
if has_key(copy, 'tmux_width')
|
|
||||||
let copy.right = remove(copy, 'tmux_width')
|
|
||||||
endif
|
|
||||||
return copy
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:error(msg)
|
function! s:error(msg)
|
||||||
echohl ErrorMsg
|
echohl ErrorMsg
|
||||||
echom a:msg
|
echom a:msg
|
||||||
@@ -229,9 +245,13 @@ function! s:common_sink(action, lines) abort
|
|||||||
endif
|
endif
|
||||||
try
|
try
|
||||||
let empty = empty(s:fzf_expand('%')) && line('$') == 1 && empty(getline(1)) && !&modified
|
let empty = empty(s:fzf_expand('%')) && line('$') == 1 && empty(getline(1)) && !&modified
|
||||||
let autochdir = &autochdir
|
" Preserve the current working directory in case it's changed during
|
||||||
set noautochdir
|
" the execution (e.g. `set autochdir` or `autocmd BufEnter * lcd ...`)
|
||||||
|
let cwd = exists('w:fzf_pushd') ? w:fzf_pushd.dir : expand('%:p:h')
|
||||||
for item in a:lines
|
for item in a:lines
|
||||||
|
if item[0] != '~' && item !~ (s:is_win ? '^[A-Z]:\' : '^/')
|
||||||
|
let item = join([cwd, item], (s:is_win ? '\' : '/'))
|
||||||
|
endif
|
||||||
if empty
|
if empty
|
||||||
execute 'e' s:escape(item)
|
execute 'e' s:escape(item)
|
||||||
let empty = 0
|
let empty = 0
|
||||||
@@ -245,7 +265,6 @@ function! s:common_sink(action, lines) abort
|
|||||||
endfor
|
endfor
|
||||||
catch /^Vim:Interrupt$/
|
catch /^Vim:Interrupt$/
|
||||||
finally
|
finally
|
||||||
let &autochdir = autochdir
|
|
||||||
silent! autocmd! fzf_swap
|
silent! autocmd! fzf_swap
|
||||||
endtry
|
endtry
|
||||||
endfunction
|
endfunction
|
||||||
@@ -290,7 +309,7 @@ function! fzf#wrap(...)
|
|||||||
let expects = map(copy(args), 'type(v:val)')
|
let expects = map(copy(args), 'type(v:val)')
|
||||||
let tidx = 0
|
let tidx = 0
|
||||||
for arg in copy(a:000)
|
for arg in copy(a:000)
|
||||||
let tidx = index(expects, type(arg), tidx)
|
let tidx = index(expects, type(arg) == 6 ? type(0) : type(arg), tidx)
|
||||||
if tidx < 0
|
if tidx < 0
|
||||||
throw 'Invalid arguments (expected: [name string] [opts dict] [fullscreen boolean])'
|
throw 'Invalid arguments (expected: [name string] [opts dict] [fullscreen boolean])'
|
||||||
endif
|
endif
|
||||||
@@ -315,7 +334,7 @@ function! fzf#wrap(...)
|
|||||||
if !exists('g:fzf_layout') && exists('g:fzf_height')
|
if !exists('g:fzf_layout') && exists('g:fzf_height')
|
||||||
let opts.down = g:fzf_height
|
let opts.down = g:fzf_height
|
||||||
else
|
else
|
||||||
let opts = extend(opts, s:validate_layout(get(g:, 'fzf_layout', s:default_layout)))
|
let opts = extend(opts, s:validate_layout(get(g:, 'fzf_layout', s:default_layout())))
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -362,11 +381,11 @@ function! fzf#run(...) abort
|
|||||||
try
|
try
|
||||||
let [shell, shellslash, shellcmdflag, shellxquote] = s:use_sh()
|
let [shell, shellslash, shellcmdflag, shellxquote] = s:use_sh()
|
||||||
|
|
||||||
let dict = exists('a:1') ? s:upgrade(a:1) : {}
|
let dict = exists('a:1') ? copy(a:1) : {}
|
||||||
let temps = { 'result': s:fzf_tempname() }
|
let temps = { 'result': s:fzf_tempname() }
|
||||||
let optstr = s:evaluate_opts(get(dict, 'options', ''))
|
let optstr = s:evaluate_opts(get(dict, 'options', ''))
|
||||||
try
|
try
|
||||||
let fzf_exec = s:fzf_exec()
|
let fzf_exec = fzf#shellescape(fzf#exec())
|
||||||
catch
|
catch
|
||||||
throw v:exception
|
throw v:exception
|
||||||
endtry
|
endtry
|
||||||
@@ -378,12 +397,6 @@ try
|
|||||||
let dict.dir = fnamemodify(dict.dir, ':p')
|
let dict.dir = fnamemodify(dict.dir, ':p')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if !has_key(dict, 'source') && !empty($FZF_DEFAULT_COMMAND) && !s:is_win
|
|
||||||
let temps.source = s:fzf_tempname()
|
|
||||||
call writefile(s:wrap_cmds(split($FZF_DEFAULT_COMMAND, "\n")), temps.source)
|
|
||||||
let dict.source = (empty($SHELL) ? &shell : $SHELL).' '.fzf#shellescape(temps.source)
|
|
||||||
endif
|
|
||||||
|
|
||||||
if has_key(dict, 'source')
|
if has_key(dict, 'source')
|
||||||
let source = dict.source
|
let source = dict.source
|
||||||
let type = type(source)
|
let type = type(source)
|
||||||
@@ -400,7 +413,7 @@ try
|
|||||||
let prefix = ''
|
let prefix = ''
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let prefer_tmux = get(g:, 'fzf_prefer_tmux', 0)
|
let prefer_tmux = get(g:, 'fzf_prefer_tmux', 0) || has_key(dict, 'tmux')
|
||||||
let use_height = has_key(dict, 'down') && !has('gui_running') &&
|
let use_height = has_key(dict, 'down') && !has('gui_running') &&
|
||||||
\ !(has('nvim') || s:is_win || has('win32unix') || s:present(dict, 'up', 'left', 'right', 'window')) &&
|
\ !(has('nvim') || s:is_win || has('win32unix') || s:present(dict, 'up', 'left', 'right', 'window')) &&
|
||||||
\ executable('tput') && filereadable('/dev/tty')
|
\ executable('tput') && filereadable('/dev/tty')
|
||||||
@@ -408,7 +421,7 @@ try
|
|||||||
let has_nvim_term = has('nvim-0.2.1') || has('nvim') && !s:is_win
|
let has_nvim_term = has('nvim-0.2.1') || has('nvim') && !s:is_win
|
||||||
let use_term = has_nvim_term ||
|
let use_term = has_nvim_term ||
|
||||||
\ has_vim8_term && !has('win32unix') && (has('gui_running') || s:is_win || !use_height && s:present(dict, 'down', 'up', 'left', 'right', 'window'))
|
\ has_vim8_term && !has('win32unix') && (has('gui_running') || s:is_win || !use_height && s:present(dict, 'down', 'up', 'left', 'right', 'window'))
|
||||||
let use_tmux = (!use_height && !use_term || prefer_tmux) && !has('win32unix') && s:tmux_enabled() && s:splittable(dict)
|
let use_tmux = (has_key(dict, 'tmux') || (!use_height && !use_term || prefer_tmux) && !has('win32unix') && s:splittable(dict)) && s:tmux_enabled()
|
||||||
if prefer_tmux && use_tmux
|
if prefer_tmux && use_tmux
|
||||||
let use_height = 0
|
let use_height = 0
|
||||||
let use_term = 0
|
let use_term = 0
|
||||||
@@ -444,7 +457,8 @@ function! s:present(dict, ...)
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:fzf_tmux(dict)
|
function! s:fzf_tmux(dict)
|
||||||
let size = ''
|
let size = get(a:dict, 'tmux', '')
|
||||||
|
if empty(size)
|
||||||
for o in ['up', 'down', 'left', 'right']
|
for o in ['up', 'down', 'left', 'right']
|
||||||
if s:present(a:dict, o)
|
if s:present(a:dict, o)
|
||||||
let spec = a:dict[o]
|
let spec = a:dict[o]
|
||||||
@@ -457,6 +471,7 @@ function! s:fzf_tmux(dict)
|
|||||||
break
|
break
|
||||||
endif
|
endif
|
||||||
endfor
|
endfor
|
||||||
|
endif
|
||||||
return printf('LINES=%d COLUMNS=%d %s %s %s --',
|
return printf('LINES=%d COLUMNS=%d %s %s %s --',
|
||||||
\ &lines, &columns, fzf#shellescape(s:fzf_tmux), size, (has_key(a:dict, 'source') ? '' : '-'))
|
\ &lines, &columns, fzf#shellescape(s:fzf_tmux), size, (has_key(a:dict, 'source') ? '' : '-'))
|
||||||
endfunction
|
endfunction
|
||||||
@@ -511,7 +526,7 @@ function! s:dopopd()
|
|||||||
if s:fzf_getcwd() ==# w:fzf_pushd.dir && (!&autochdir || w:fzf_pushd.bufname ==# bufname(''))
|
if s:fzf_getcwd() ==# w:fzf_pushd.dir && (!&autochdir || w:fzf_pushd.bufname ==# bufname(''))
|
||||||
execute w:fzf_pushd.command s:escape(w:fzf_pushd.origin)
|
execute w:fzf_pushd.command s:escape(w:fzf_pushd.origin)
|
||||||
endif
|
endif
|
||||||
unlet w:fzf_pushd
|
unlet! w:fzf_pushd
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:xterm_launcher()
|
function! s:xterm_launcher()
|
||||||
@@ -626,14 +641,19 @@ function! s:calc_size(max, val, dict)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
let opts = $FZF_DEFAULT_OPTS.' '.s:evaluate_opts(get(a:dict, 'options', ''))
|
let opts = $FZF_DEFAULT_OPTS.' '.s:evaluate_opts(get(a:dict, 'options', ''))
|
||||||
let margin = stridx(opts, '--inline-info') > stridx(opts, '--no-inline-info') ? 1 : 2
|
if opts =~ 'preview'
|
||||||
|
return size
|
||||||
|
endif
|
||||||
|
let margin = match(opts, '--inline-info\|--info[^-]\{-}inline') > match(opts, '--no-inline-info\|--info[^-]\{-}\(default\|hidden\)') ? 1 : 2
|
||||||
let margin += stridx(opts, '--border') > stridx(opts, '--no-border') ? 2 : 0
|
let margin += stridx(opts, '--border') > stridx(opts, '--no-border') ? 2 : 0
|
||||||
let margin += stridx(opts, '--header') > stridx(opts, '--no-header')
|
if stridx(opts, '--header') > stridx(opts, '--no-header')
|
||||||
|
let margin += len(split(opts, "\n"))
|
||||||
|
endif
|
||||||
return srcsz >= 0 ? min([srcsz + margin, size]) : size
|
return srcsz >= 0 ? min([srcsz + margin, size]) : size
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:getpos()
|
function! s:getpos()
|
||||||
return {'tab': tabpagenr(), 'win': winnr(), 'cnt': winnr('$'), 'tcnt': tabpagenr('$')}
|
return {'tab': tabpagenr(), 'win': winnr(), 'winid': win_getid(), 'cnt': winnr('$'), 'tcnt': tabpagenr('$')}
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:split(dict)
|
function! s:split(dict)
|
||||||
@@ -643,9 +663,18 @@ function! s:split(dict)
|
|||||||
\ 'left': ['vertical topleft', 'vertical resize', &columns],
|
\ 'left': ['vertical topleft', 'vertical resize', &columns],
|
||||||
\ 'right': ['vertical botright', 'vertical resize', &columns] }
|
\ 'right': ['vertical botright', 'vertical resize', &columns] }
|
||||||
let ppos = s:getpos()
|
let ppos = s:getpos()
|
||||||
|
let is_popup = 0
|
||||||
try
|
try
|
||||||
if s:present(a:dict, 'window')
|
if s:present(a:dict, 'window')
|
||||||
|
if type(a:dict.window) == type({})
|
||||||
|
if !s:popup_support()
|
||||||
|
throw 'Nvim 0.4+ or Vim 8.2.191+ with popupwin feature is required for pop-up window'
|
||||||
|
end
|
||||||
|
call s:popup(a:dict.window)
|
||||||
|
let is_popup = 1
|
||||||
|
else
|
||||||
execute 'keepalt' a:dict.window
|
execute 'keepalt' a:dict.window
|
||||||
|
endif
|
||||||
elseif !s:splittable(a:dict)
|
elseif !s:splittable(a:dict)
|
||||||
execute (tabpagenr()-1).'tabnew'
|
execute (tabpagenr()-1).'tabnew'
|
||||||
else
|
else
|
||||||
@@ -660,20 +689,22 @@ function! s:split(dict)
|
|||||||
endif
|
endif
|
||||||
execute cmd sz.'new'
|
execute cmd sz.'new'
|
||||||
execute resz sz
|
execute resz sz
|
||||||
return [ppos, {}]
|
return [ppos, {}, is_popup]
|
||||||
endif
|
endif
|
||||||
endfor
|
endfor
|
||||||
endif
|
endif
|
||||||
return [ppos, { '&l:wfw': &l:wfw, '&l:wfh': &l:wfh }]
|
return [ppos, is_popup ? {} : { '&l:wfw': &l:wfw, '&l:wfh': &l:wfh }, is_popup]
|
||||||
finally
|
finally
|
||||||
|
if !is_popup
|
||||||
setlocal winfixwidth winfixheight
|
setlocal winfixwidth winfixheight
|
||||||
|
endif
|
||||||
endtry
|
endtry
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:execute_term(dict, command, temps) abort
|
function! s:execute_term(dict, command, temps) abort
|
||||||
let winrest = winrestcmd()
|
let winrest = winrestcmd()
|
||||||
let pbuf = bufnr('')
|
let pbuf = bufnr('')
|
||||||
let [ppos, winopts] = s:split(a:dict)
|
let [ppos, winopts, is_popup] = s:split(a:dict)
|
||||||
call s:use_sh()
|
call s:use_sh()
|
||||||
let b:fzf = a:dict
|
let b:fzf = a:dict
|
||||||
let fzf = { 'buf': bufnr(''), 'pbuf': pbuf, 'ppos': ppos, 'dict': a:dict, 'temps': a:temps,
|
let fzf = { 'buf': bufnr(''), 'pbuf': pbuf, 'ppos': ppos, 'dict': a:dict, 'temps': a:temps,
|
||||||
@@ -702,8 +733,8 @@ function! s:execute_term(dict, command, temps) abort
|
|||||||
" there's no other listed buffer (nvim +'set nobuflisted')
|
" there's no other listed buffer (nvim +'set nobuflisted')
|
||||||
close
|
close
|
||||||
endif
|
endif
|
||||||
execute 'tabnext' self.ppos.tab
|
silent! execute 'tabnext' self.ppos.tab
|
||||||
execute self.ppos.win.'wincmd w'
|
silent! execute self.ppos.win.'wincmd w'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if bufexists(self.buf)
|
if bufexists(self.buf)
|
||||||
@@ -737,8 +768,17 @@ function! s:execute_term(dict, command, temps) abort
|
|||||||
if has('nvim')
|
if has('nvim')
|
||||||
call termopen(command, fzf)
|
call termopen(command, fzf)
|
||||||
else
|
else
|
||||||
let fzf.buf = term_start([&shell, &shellcmdflag, command], {'curwin': 1, 'exit_cb': function(fzf.on_exit)})
|
let term_opts = {'exit_cb': function(fzf.on_exit)}
|
||||||
if !has('patch-8.0.1261') && !has('nvim') && !s:is_win
|
if is_popup
|
||||||
|
let term_opts.hidden = 1
|
||||||
|
else
|
||||||
|
let term_opts.curwin = 1
|
||||||
|
endif
|
||||||
|
let fzf.buf = term_start([&shell, &shellcmdflag, command], term_opts)
|
||||||
|
if is_popup && exists('#TerminalWinOpen')
|
||||||
|
doautocmd <nomodeline> TerminalWinOpen
|
||||||
|
endif
|
||||||
|
if !has('patch-8.0.1261') && !s:is_win
|
||||||
call term_wait(fzf.buf, 20)
|
call term_wait(fzf.buf, 20)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
@@ -793,6 +833,99 @@ function! s:callback(dict, lines) abort
|
|||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
if has('nvim')
|
||||||
|
function s:create_popup(hl, opts) abort
|
||||||
|
let buf = nvim_create_buf(v:false, v:true)
|
||||||
|
let opts = extend({'relative': 'editor', 'style': 'minimal'}, a:opts)
|
||||||
|
let border = has_key(opts, 'border') ? remove(opts, 'border') : []
|
||||||
|
let win = nvim_open_win(buf, v:true, opts)
|
||||||
|
call setwinvar(win, '&winhighlight', 'NormalFloat:'..a:hl)
|
||||||
|
call setwinvar(win, '&colorcolumn', '')
|
||||||
|
if !empty(border)
|
||||||
|
call nvim_buf_set_lines(buf, 0, -1, v:true, border)
|
||||||
|
endif
|
||||||
|
return buf
|
||||||
|
endfunction
|
||||||
|
else
|
||||||
|
function! s:create_popup(hl, opts) abort
|
||||||
|
let is_frame = has_key(a:opts, 'border')
|
||||||
|
let s:popup_create = {buf -> popup_create(buf, #{
|
||||||
|
\ line: a:opts.row,
|
||||||
|
\ col: a:opts.col,
|
||||||
|
\ minwidth: a:opts.width,
|
||||||
|
\ minheight: a:opts.height,
|
||||||
|
\ zindex: 50 - is_frame,
|
||||||
|
\ })}
|
||||||
|
if is_frame
|
||||||
|
let id = s:popup_create('')
|
||||||
|
call setwinvar(id, '&wincolor', a:hl)
|
||||||
|
call setbufline(winbufnr(id), 1, a:opts.border)
|
||||||
|
execute 'autocmd BufWipeout * ++once call popup_close('..id..')'
|
||||||
|
return winbufnr(id)
|
||||||
|
else
|
||||||
|
autocmd TerminalOpen * ++once call s:popup_create(str2nr(expand('<abuf>')))
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
endif
|
||||||
|
|
||||||
|
function! s:popup(opts) abort
|
||||||
|
" Support ambiwidth == 'double'
|
||||||
|
let ambidouble = &ambiwidth == 'double' ? 2 : 1
|
||||||
|
|
||||||
|
" Size and position
|
||||||
|
let width = min([max([8, a:opts.width > 1 ? a:opts.width : float2nr(&columns * a:opts.width)]), &columns])
|
||||||
|
let width += width % ambidouble
|
||||||
|
let height = min([max([4, a:opts.height > 1 ? a:opts.height : float2nr(&lines * a:opts.height)]), &lines - has('nvim')])
|
||||||
|
let row = float2nr(get(a:opts, 'yoffset', 0.5) * (&lines - height))
|
||||||
|
let col = float2nr(get(a:opts, 'xoffset', 0.5) * (&columns - width))
|
||||||
|
|
||||||
|
" Managing the differences
|
||||||
|
let row = min([max([0, row]), &lines - has('nvim') - height])
|
||||||
|
let col = min([max([0, col]), &columns - width])
|
||||||
|
let row += !has('nvim')
|
||||||
|
let col += !has('nvim')
|
||||||
|
|
||||||
|
" Border style
|
||||||
|
let style = tolower(get(a:opts, 'border', 'rounded'))
|
||||||
|
if !has_key(a:opts, 'border') && !get(a:opts, 'rounded', 1)
|
||||||
|
let style = 'sharp'
|
||||||
|
endif
|
||||||
|
|
||||||
|
if style =~ 'vertical\|left\|right'
|
||||||
|
let mid = style == 'vertical' ? '│' .. repeat(' ', width - 2 * ambidouble) .. '│' :
|
||||||
|
\ style == 'left' ? '│' .. repeat(' ', width - 1 * ambidouble)
|
||||||
|
\ : repeat(' ', width - 1 * ambidouble) .. '│'
|
||||||
|
let border = repeat([mid], height)
|
||||||
|
let shift = { 'row': 0, 'col': style == 'right' ? 0 : 2, 'width': style == 'vertical' ? -4 : -2, 'height': 0 }
|
||||||
|
elseif style =~ 'horizontal\|top\|bottom'
|
||||||
|
let hor = repeat('─', width / ambidouble)
|
||||||
|
let mid = repeat(' ', width)
|
||||||
|
let border = style == 'horizontal' ? [hor] + repeat([mid], height - 2) + [hor] :
|
||||||
|
\ style == 'top' ? [hor] + repeat([mid], height - 1)
|
||||||
|
\ : repeat([mid], height - 1) + [hor]
|
||||||
|
let shift = { 'row': style == 'bottom' ? 0 : 1, 'col': 0, 'width': 0, 'height': style == 'horizontal' ? -2 : -1 }
|
||||||
|
else
|
||||||
|
let edges = style == 'sharp' ? ['┌', '┐', '└', '┘'] : ['╭', '╮', '╰', '╯']
|
||||||
|
let bar = repeat('─', width / ambidouble - 2)
|
||||||
|
let top = edges[0] .. bar .. edges[1]
|
||||||
|
let mid = '│' .. repeat(' ', width - 2 * ambidouble) .. '│'
|
||||||
|
let bot = edges[2] .. bar .. edges[3]
|
||||||
|
let border = [top] + repeat([mid], height - 2) + [bot]
|
||||||
|
let shift = { 'row': 1, 'col': 2, 'width': -4, 'height': -2 }
|
||||||
|
endif
|
||||||
|
|
||||||
|
let highlight = get(a:opts, 'highlight', 'Comment')
|
||||||
|
let frame = s:create_popup(highlight, {
|
||||||
|
\ 'row': row, 'col': col, 'width': width, 'height': height, 'border': border
|
||||||
|
\ })
|
||||||
|
call s:create_popup('Normal', {
|
||||||
|
\ 'row': row + shift.row, 'col': col + shift.col, 'width': width + shift.width, 'height': height + shift.height
|
||||||
|
\ })
|
||||||
|
if has('nvim')
|
||||||
|
execute 'autocmd BufWipeout <buffer> bwipeout '..frame
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
let s:default_action = {
|
let s:default_action = {
|
||||||
\ 'ctrl-t': 'tab split',
|
\ 'ctrl-t': 'tab split',
|
||||||
\ 'ctrl-x': 'split',
|
\ 'ctrl-x': 'split',
|
||||||
|
@@ -2,10 +2,10 @@
|
|||||||
# / __/___ / __/
|
# / __/___ / __/
|
||||||
# / /_/_ / / /_
|
# / /_/_ / / /_
|
||||||
# / __/ / /_/ __/
|
# / __/ / /_/ __/
|
||||||
# /_/ /___/_/-completion.bash
|
# /_/ /___/_/ completion.bash
|
||||||
#
|
#
|
||||||
# - $FZF_TMUX (default: 0)
|
# - $FZF_TMUX (default: 0)
|
||||||
# - $FZF_TMUX_HEIGHT (default: '40%')
|
# - $FZF_TMUX_OPTS (default: empty)
|
||||||
# - $FZF_COMPLETION_TRIGGER (default: '**')
|
# - $FZF_COMPLETION_TRIGGER (default: '**')
|
||||||
# - $FZF_COMPLETION_OPTS (default: empty)
|
# - $FZF_COMPLETION_OPTS (default: empty)
|
||||||
|
|
||||||
@@ -34,9 +34,16 @@ fi
|
|||||||
# To redraw line after fzf closes (printf '\e[5n')
|
# To redraw line after fzf closes (printf '\e[5n')
|
||||||
bind '"\e[0n": redraw-current-line'
|
bind '"\e[0n": redraw-current-line'
|
||||||
|
|
||||||
__fzfcmd_complete() {
|
__fzf_comprun() {
|
||||||
[ -n "$TMUX_PANE" ] && [ "${FZF_TMUX:-0}" != 0 ] && [ ${LINES:-40} -gt 15 ] &&
|
if [ "$(type -t _fzf_comprun 2>&1)" = function ]; then
|
||||||
echo "fzf-tmux -d${FZF_TMUX_HEIGHT:-40%}" || echo "fzf"
|
_fzf_comprun "$@"
|
||||||
|
elif [ -n "$TMUX_PANE" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "$FZF_TMUX_OPTS" ]; }; then
|
||||||
|
shift
|
||||||
|
fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- "$@"
|
||||||
|
else
|
||||||
|
shift
|
||||||
|
fzf "$@"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
__fzf_orig_completion_filter() {
|
__fzf_orig_completion_filter() {
|
||||||
@@ -72,6 +79,8 @@ _fzf_opts_completion() {
|
|||||||
--margin
|
--margin
|
||||||
--inline-info
|
--inline-info
|
||||||
--prompt
|
--prompt
|
||||||
|
--pointer
|
||||||
|
--marker
|
||||||
--header
|
--header
|
||||||
--header-lines
|
--header-lines
|
||||||
--ansi
|
--ansi
|
||||||
@@ -140,8 +149,7 @@ _fzf_handle_dynamic_completion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
__fzf_generic_path_completion() {
|
__fzf_generic_path_completion() {
|
||||||
local cur base dir leftover matches trigger cmd fzf
|
local cur base dir leftover matches trigger cmd
|
||||||
fzf="$(__fzfcmd_complete)"
|
|
||||||
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
|
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
||||||
@@ -157,7 +165,7 @@ __fzf_generic_path_completion() {
|
|||||||
leftover=${leftover/#\/}
|
leftover=${leftover/#\/}
|
||||||
[ -z "$dir" ] && dir='.'
|
[ -z "$dir" ] && dir='.'
|
||||||
[ "$dir" != "/" ] && dir="${dir/%\//}"
|
[ "$dir" != "/" ] && dir="${dir/%\//}"
|
||||||
matches=$(eval "$1 $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" $fzf $2 -q "$leftover" | while read -r item; do
|
matches=$(eval "$1 $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $2" __fzf_comprun "$4" -q "$leftover" | while read -r item; do
|
||||||
printf "%q$3 " "$item"
|
printf "%q$3 " "$item"
|
||||||
done)
|
done)
|
||||||
matches=${matches% }
|
matches=${matches% }
|
||||||
@@ -182,10 +190,30 @@ __fzf_generic_path_completion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete() {
|
_fzf_complete() {
|
||||||
local cur selected trigger cmd fzf post
|
# Split arguments around --
|
||||||
|
local args rest str_arg i sep
|
||||||
|
args=("$@")
|
||||||
|
sep=
|
||||||
|
for i in "${!args[@]}"; do
|
||||||
|
if [[ "${args[$i]}" = -- ]]; then
|
||||||
|
sep=$i
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ -n "$sep" ]]; then
|
||||||
|
str_arg=
|
||||||
|
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
|
||||||
|
args=("${args[@]:0:$sep}")
|
||||||
|
else
|
||||||
|
str_arg=$1
|
||||||
|
args=()
|
||||||
|
shift
|
||||||
|
rest=("$@")
|
||||||
|
fi
|
||||||
|
|
||||||
|
local cur selected trigger cmd post
|
||||||
post="$(caller 0 | awk '{print $2}')_post"
|
post="$(caller 0 | awk '{print $2}')_post"
|
||||||
type -t "$post" > /dev/null 2>&1 || post=cat
|
type -t "$post" > /dev/null 2>&1 || post=cat
|
||||||
fzf="$(__fzfcmd_complete)"
|
|
||||||
|
|
||||||
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
|
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
|
||||||
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
||||||
@@ -193,7 +221,7 @@ _fzf_complete() {
|
|||||||
if [[ "$cur" == *"$trigger" ]]; then
|
if [[ "$cur" == *"$trigger" ]]; then
|
||||||
cur=${cur:0:${#cur}-${#trigger}}
|
cur=${cur:0:${#cur}-${#trigger}}
|
||||||
|
|
||||||
selected=$(cat | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" $fzf $1 -q "$cur" | $post | tr '\n' ' ')
|
selected=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $str_arg" __fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | $post | tr '\n' ' ')
|
||||||
selected=${selected% } # Strip trailing space not to repeat "-o nospace"
|
selected=${selected% } # Strip trailing space not to repeat "-o nospace"
|
||||||
if [ -n "$selected" ]; then
|
if [ -n "$selected" ]; then
|
||||||
COMPREPLY=("$selected")
|
COMPREPLY=("$selected")
|
||||||
@@ -203,8 +231,7 @@ _fzf_complete() {
|
|||||||
printf '\e[5n'
|
printf '\e[5n'
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
shift
|
_fzf_handle_dynamic_completion "$cmd" "${rest[@]}"
|
||||||
_fzf_handle_dynamic_completion "$cmd" "$@"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,49 +249,44 @@ _fzf_dir_completion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_kill() {
|
_fzf_complete_kill() {
|
||||||
[ -n "${COMP_WORDS[COMP_CWORD]}" ] && return 1
|
local trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
||||||
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
local selected fzf
|
if [[ -z "$cur" ]]; then
|
||||||
fzf="$(__fzfcmd_complete)"
|
COMP_WORDS[$COMP_CWORD]=$trigger
|
||||||
selected=$(command ps -ef | sed 1d | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-50%} --min-height 15 --reverse $FZF_DEFAULT_OPTS --preview 'echo {}' --preview-window down:3:wrap $FZF_COMPLETION_OPTS" $fzf -m | awk '{print $2}' | tr '\n' ' ')
|
elif [[ "$cur" != *"$trigger" ]]; then
|
||||||
printf '\e[5n'
|
return 1
|
||||||
|
|
||||||
if [ -n "$selected" ]; then
|
|
||||||
COMPREPLY=( "$selected" )
|
|
||||||
return 0
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
_fzf_proc_completion "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_telnet() {
|
_fzf_proc_completion() {
|
||||||
_fzf_complete '+m' "$@" < <(
|
_fzf_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <(
|
||||||
command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0' |
|
command ps -ef | sed 1d
|
||||||
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_ssh() {
|
_fzf_proc_completion_post() {
|
||||||
_fzf_complete '+m' "$@" < <(
|
awk '{print $2}'
|
||||||
cat <(cat ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
|
}
|
||||||
|
|
||||||
|
_fzf_host_completion() {
|
||||||
|
_fzf_complete +m -- "$@" < <(
|
||||||
|
command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
|
||||||
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
|
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
|
||||||
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
|
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
|
||||||
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_unset() {
|
_fzf_var_completion() {
|
||||||
_fzf_complete '-m' "$@" < <(
|
_fzf_complete -m -- "$@" < <(
|
||||||
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_export() {
|
_fzf_alias_completion() {
|
||||||
_fzf_complete '-m' "$@" < <(
|
_fzf_complete -m -- "$@" < <(
|
||||||
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
_fzf_complete_unalias() {
|
|
||||||
_fzf_complete '-m' "$@" < <(
|
|
||||||
alias | sed 's/=.*//' | sed 's/.* //'
|
alias | sed 's/=.*//' | sed 's/.* //'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -282,11 +304,10 @@ a_cmds="
|
|||||||
find git grep gunzip gzip hg jar
|
find git grep gunzip gzip hg jar
|
||||||
ln ls mv open rm rsync scp
|
ln ls mv open rm rsync scp
|
||||||
svn tar unzip zip"
|
svn tar unzip zip"
|
||||||
x_cmds="kill ssh telnet unset unalias export"
|
|
||||||
|
|
||||||
# Preserve existing completion
|
# Preserve existing completion
|
||||||
eval "$(complete |
|
eval "$(complete |
|
||||||
sed -E '/-F/!d; / _fzf/d; '"/ ($(echo $d_cmds $a_cmds $x_cmds | sed 's/ /|/g; s/+/\\+/g'))$/"'!d' |
|
sed -E '/-F/!d; / _fzf/d; '"/ ($(echo $d_cmds $a_cmds | sed 's/ /|/g; s/+/\\+/g'))$/"'!d' |
|
||||||
__fzf_orig_completion_filter)"
|
__fzf_orig_completion_filter)"
|
||||||
|
|
||||||
if type _completion_loader > /dev/null 2>&1; then
|
if type _completion_loader > /dev/null 2>&1; then
|
||||||
@@ -318,36 +339,34 @@ for cmd in $d_cmds; do
|
|||||||
__fzf_defc "$cmd" _fzf_dir_completion "-o nospace -o dirnames"
|
__fzf_defc "$cmd" _fzf_dir_completion "-o nospace -o dirnames"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Kill completion
|
# Kill completion (supports empty completion trigger)
|
||||||
complete -F _fzf_complete_kill -o nospace -o default -o bashdefault kill
|
complete -F _fzf_complete_kill -o default -o bashdefault kill
|
||||||
|
|
||||||
# Host completion
|
unset cmd d_cmds a_cmds
|
||||||
complete -F _fzf_complete_ssh -o default -o bashdefault ssh
|
|
||||||
complete -F _fzf_complete_telnet -o default -o bashdefault telnet
|
|
||||||
|
|
||||||
# Environment variables / Aliases
|
|
||||||
complete -F _fzf_complete_unset -o default -o bashdefault unset
|
|
||||||
complete -F _fzf_complete_export -o default -o bashdefault export
|
|
||||||
complete -F _fzf_complete_unalias -o default -o bashdefault unalias
|
|
||||||
|
|
||||||
unset cmd d_cmds a_cmds x_cmds
|
|
||||||
|
|
||||||
_fzf_setup_completion() {
|
_fzf_setup_completion() {
|
||||||
local kind fn cmd
|
local kind fn cmd
|
||||||
kind=$1
|
kind=$1
|
||||||
fn=_fzf_${1}_completion
|
fn=_fzf_${1}_completion
|
||||||
if [[ $# -lt 2 ]] || ! type -t "$fn" > /dev/null; then
|
if [[ $# -lt 2 ]] || ! type -t "$fn" > /dev/null; then
|
||||||
echo "usage: ${FUNCNAME[0]} path|dir COMMANDS..."
|
echo "usage: ${FUNCNAME[0]} path|dir|var|alias|host|proc COMMANDS..."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
shift
|
shift
|
||||||
|
eval "$(complete -p "$@" 2> /dev/null | grep -v "$fn" | __fzf_orig_completion_filter)"
|
||||||
for cmd in "$@"; do
|
for cmd in "$@"; do
|
||||||
eval "$(complete -p "$cmd" 2> /dev/null | grep -v "$fn" | __fzf_orig_completion_filter)"
|
|
||||||
case "$kind" in
|
case "$kind" in
|
||||||
dir) __fzf_defc "$cmd" "$fn" "-o nospace -o dirnames" ;;
|
dir) __fzf_defc "$cmd" "$fn" "-o nospace -o dirnames" ;;
|
||||||
|
var) __fzf_defc "$cmd" "$fn" "-o default -o nospace -v" ;;
|
||||||
|
alias) __fzf_defc "$cmd" "$fn" "-a" ;;
|
||||||
*) __fzf_defc "$cmd" "$fn" "-o default -o bashdefault" ;;
|
*) __fzf_defc "$cmd" "$fn" "-o default -o bashdefault" ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Environment variables / Aliases / Hosts
|
||||||
|
_fzf_setup_completion 'var' export unset
|
||||||
|
_fzf_setup_completion 'alias' unalias
|
||||||
|
_fzf_setup_completion 'host' ssh telnet
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
@@ -2,14 +2,79 @@
|
|||||||
# / __/___ / __/
|
# / __/___ / __/
|
||||||
# / /_/_ / / /_
|
# / /_/_ / / /_
|
||||||
# / __/ / /_/ __/
|
# / __/ / /_/ __/
|
||||||
# /_/ /___/_/-completion.zsh
|
# /_/ /___/_/ completion.zsh
|
||||||
#
|
#
|
||||||
# - $FZF_TMUX (default: 0)
|
# - $FZF_TMUX (default: 0)
|
||||||
# - $FZF_TMUX_HEIGHT (default: '40%')
|
# - $FZF_TMUX_OPTS (default: '-d 40%')
|
||||||
# - $FZF_COMPLETION_TRIGGER (default: '**')
|
# - $FZF_COMPLETION_TRIGGER (default: '**')
|
||||||
# - $FZF_COMPLETION_OPTS (default: empty)
|
# - $FZF_COMPLETION_OPTS (default: empty)
|
||||||
|
|
||||||
if [[ $- =~ i ]]; then
|
# Both branches of the following `if` do the same thing -- define
|
||||||
|
# __fzf_completion_options such that `eval $__fzf_completion_options` sets
|
||||||
|
# all options to the same values they currently have. We'll do just that at
|
||||||
|
# the bottom of the file after changing options to what we prefer.
|
||||||
|
#
|
||||||
|
# IMPORTANT: Until we get to the `emulate` line, all words that *can* be quoted
|
||||||
|
# *must* be quoted in order to prevent alias expansion. In addition, code must
|
||||||
|
# be written in a way works with any set of zsh options. This is very tricky, so
|
||||||
|
# careful when you change it.
|
||||||
|
#
|
||||||
|
# Start by loading the builtin zsh/parameter module. It provides `options`
|
||||||
|
# associative array that stores current shell options.
|
||||||
|
if 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then
|
||||||
|
# This is the fast branch and it gets taken on virtually all Zsh installations.
|
||||||
|
#
|
||||||
|
# ${(kv)options[@]} expands to array of keys (option names) and values ("on"
|
||||||
|
# or "off"). The subsequent expansion# with (j: :) flag joins all elements
|
||||||
|
# together separated by spaces. __fzf_completion_options ends up with a value
|
||||||
|
# like this: "options=(shwordsplit off aliases on ...)".
|
||||||
|
__fzf_completion_options="options=(${(j: :)${(kv)options[@]}})"
|
||||||
|
else
|
||||||
|
# This branch is much slower because it forks to get the names of all
|
||||||
|
# zsh options. It's possible to eliminate this fork but it's not worth the
|
||||||
|
# trouble because this branch gets taken only on very ancient or broken
|
||||||
|
# zsh installations.
|
||||||
|
() {
|
||||||
|
# That `()` above defines an anonymous function. This is essentially a scope
|
||||||
|
# for local parameters. We use it to avoid polluting global scope.
|
||||||
|
'local' '__fzf_opt'
|
||||||
|
__fzf_completion_options="setopt"
|
||||||
|
# `set -o` prints one line for every zsh option. Each line contains option
|
||||||
|
# name, some spaces, and then either "on" or "off". We just want option names.
|
||||||
|
# Expansion with (@f) flag splits a string into lines. The outer expansion
|
||||||
|
# removes spaces and everything that follow them on every line. __fzf_opt
|
||||||
|
# ends up iterating over option names: shwordsplit, aliases, etc.
|
||||||
|
for __fzf_opt in "${(@)${(@f)$(set -o)}%% *}"; do
|
||||||
|
if [[ -o "$__fzf_opt" ]]; then
|
||||||
|
# Option $__fzf_opt is currently on, so remember to set it back on.
|
||||||
|
__fzf_completion_options+=" -o $__fzf_opt"
|
||||||
|
else
|
||||||
|
# Option $__fzf_opt is currently off, so remember to set it back off.
|
||||||
|
__fzf_completion_options+=" +o $__fzf_opt"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# The value of __fzf_completion_options here looks like this:
|
||||||
|
# "setopt +o shwordsplit -o aliases ..."
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enable the default zsh options (those marked with <Z> in `man zshoptions`)
|
||||||
|
# but without `aliases`. Aliases in functions are expanded when functions are
|
||||||
|
# defined, so if we disable aliases here, we'll be sure to have no pesky
|
||||||
|
# aliases in any of our functions. This way we won't need prefix every
|
||||||
|
# command with `command` or to quote every word to defend against global
|
||||||
|
# aliases. Note that `aliases` is not the only option that's important to
|
||||||
|
# control. There are several others that could wreck havoc if they are set
|
||||||
|
# to values we don't expect. With the following `emulate` command we
|
||||||
|
# sidestep this issue entirely.
|
||||||
|
'emulate' 'zsh' '-o' 'no_aliases'
|
||||||
|
|
||||||
|
# This brace is the start of try-always block. The `always` part is like
|
||||||
|
# `finally` in lesser languages. We use it to *always* restore user options.
|
||||||
|
{
|
||||||
|
|
||||||
|
# Bail out if not interactive shell.
|
||||||
|
[[ -o interactive ]] || return 0
|
||||||
|
|
||||||
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
|
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
|
||||||
if ! declare -f _fzf_compgen_path > /dev/null; then
|
if ! declare -f _fzf_compgen_path > /dev/null; then
|
||||||
@@ -31,20 +96,45 @@ fi
|
|||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
__fzfcmd_complete() {
|
__fzf_comprun() {
|
||||||
[ -n "$TMUX_PANE" ] && [ "${FZF_TMUX:-0}" != 0 ] && [ ${LINES:-40} -gt 15 ] &&
|
if [[ "$(type _fzf_comprun 2>&1)" =~ function ]]; then
|
||||||
echo "fzf-tmux -d${FZF_TMUX_HEIGHT:-40%}" || echo "fzf"
|
_fzf_comprun "$@"
|
||||||
|
elif [ -n "$TMUX_PANE" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "$FZF_TMUX_OPTS" ]; }; then
|
||||||
|
shift
|
||||||
|
if [ -n "$FZF_TMUX_OPTS" ]; then
|
||||||
|
fzf-tmux ${(Q)${(Z+n+)FZF_TMUX_OPTS}} -- "$@"
|
||||||
|
else
|
||||||
|
fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%} -- "$@"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
shift
|
||||||
|
fzf "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract the name of the command. e.g. foo=1 bar baz**<tab>
|
||||||
|
__fzf_extract_command() {
|
||||||
|
local token tokens
|
||||||
|
tokens=(${(z)1})
|
||||||
|
for token in $tokens; do
|
||||||
|
token=${(Q)token}
|
||||||
|
if [[ "$token" =~ [[:alnum:]] && ! "$token" =~ "=" ]]; then
|
||||||
|
echo "$token"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "${tokens[1]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
__fzf_generic_path_completion() {
|
__fzf_generic_path_completion() {
|
||||||
local base lbuf compgen fzf_opts suffix tail fzf dir leftover matches
|
local base lbuf cmd compgen fzf_opts suffix tail dir leftover matches
|
||||||
base=$1
|
base=$1
|
||||||
lbuf=$2
|
lbuf=$2
|
||||||
|
cmd=$(__fzf_extract_command "$lbuf")
|
||||||
compgen=$3
|
compgen=$3
|
||||||
fzf_opts=$4
|
fzf_opts=$4
|
||||||
suffix=$5
|
suffix=$5
|
||||||
tail=$6
|
tail=$6
|
||||||
fzf="$(__fzfcmd_complete)"
|
|
||||||
|
|
||||||
setopt localoptions nonomatch
|
setopt localoptions nonomatch
|
||||||
eval "base=$base"
|
eval "base=$base"
|
||||||
@@ -55,7 +145,7 @@ __fzf_generic_path_completion() {
|
|||||||
leftover=${leftover/#\/}
|
leftover=${leftover/#\/}
|
||||||
[ -z "$dir" ] && dir='.'
|
[ -z "$dir" ] && dir='.'
|
||||||
[ "$dir" != "/" ] && dir="${dir/%\//}"
|
[ "$dir" != "/" ] && dir="${dir/%\//}"
|
||||||
matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" ${=fzf} ${=fzf_opts} -q "$leftover" | while read item; do
|
matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" | while read item; do
|
||||||
echo -n "${(q)item}$suffix "
|
echo -n "${(q)item}$suffix "
|
||||||
done)
|
done)
|
||||||
matches=${matches% }
|
matches=${matches% }
|
||||||
@@ -87,17 +177,37 @@ _fzf_feed_fifo() (
|
|||||||
)
|
)
|
||||||
|
|
||||||
_fzf_complete() {
|
_fzf_complete() {
|
||||||
local fifo fzf_opts lbuf fzf matches post
|
setopt localoptions ksh_arrays
|
||||||
|
# Split arguments around --
|
||||||
|
local args rest str_arg i sep
|
||||||
|
args=("$@")
|
||||||
|
sep=
|
||||||
|
for i in {0..${#args[@]}}; do
|
||||||
|
if [[ "${args[$i]}" = -- ]]; then
|
||||||
|
sep=$i
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ -n "$sep" ]]; then
|
||||||
|
str_arg=
|
||||||
|
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
|
||||||
|
args=("${args[@]:0:$sep}")
|
||||||
|
else
|
||||||
|
str_arg=$1
|
||||||
|
args=()
|
||||||
|
shift
|
||||||
|
rest=("$@")
|
||||||
|
fi
|
||||||
|
|
||||||
|
local fifo lbuf cmd matches post
|
||||||
fifo="${TMPDIR:-/tmp}/fzf-complete-fifo-$$"
|
fifo="${TMPDIR:-/tmp}/fzf-complete-fifo-$$"
|
||||||
fzf_opts=$1
|
lbuf=${rest[0]}
|
||||||
lbuf=$2
|
cmd=$(__fzf_extract_command "$lbuf")
|
||||||
post="${funcstack[2]}_post"
|
post="${funcstack[1]}_post"
|
||||||
type $post > /dev/null 2>&1 || post=cat
|
type $post > /dev/null 2>&1 || post=cat
|
||||||
|
|
||||||
fzf="$(__fzfcmd_complete)"
|
|
||||||
|
|
||||||
_fzf_feed_fifo "$fifo"
|
_fzf_feed_fifo "$fifo"
|
||||||
matches=$(cat "$fifo" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" ${=fzf} ${=fzf_opts} -q "${(Q)prefix}" | $post | tr '\n' ' ')
|
matches=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $str_arg" __fzf_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
|
||||||
if [ -n "$matches" ]; then
|
if [ -n "$matches" ]; then
|
||||||
LBUFFER="$lbuf$matches"
|
LBUFFER="$lbuf$matches"
|
||||||
fi
|
fi
|
||||||
@@ -106,16 +216,16 @@ _fzf_complete() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_telnet() {
|
_fzf_complete_telnet() {
|
||||||
_fzf_complete '+m' "$@" < <(
|
_fzf_complete +m -- "$@" < <(
|
||||||
command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0' |
|
command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0' |
|
||||||
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_ssh() {
|
_fzf_complete_ssh() {
|
||||||
_fzf_complete '+m' "$@" < <(
|
_fzf_complete +m -- "$@" < <(
|
||||||
setopt localoptions nonomatch
|
setopt localoptions nonomatch
|
||||||
command cat <(cat ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
|
command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
|
||||||
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
|
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
|
||||||
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
|
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
|
||||||
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
||||||
@@ -123,25 +233,35 @@ _fzf_complete_ssh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_export() {
|
_fzf_complete_export() {
|
||||||
_fzf_complete '-m' "$@" < <(
|
_fzf_complete -m -- "$@" < <(
|
||||||
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_unset() {
|
_fzf_complete_unset() {
|
||||||
_fzf_complete '-m' "$@" < <(
|
_fzf_complete -m -- "$@" < <(
|
||||||
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_unalias() {
|
_fzf_complete_unalias() {
|
||||||
_fzf_complete '+m' "$@" < <(
|
_fzf_complete +m -- "$@" < <(
|
||||||
alias | sed 's/=.*//'
|
alias | sed 's/=.*//'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_fzf_complete_kill() {
|
||||||
|
_fzf_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <(
|
||||||
|
command ps -ef | sed 1d
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_kill_post() {
|
||||||
|
awk '{print $2}'
|
||||||
|
}
|
||||||
|
|
||||||
fzf-completion() {
|
fzf-completion() {
|
||||||
local tokens cmd prefix trigger tail fzf matches lbuf d_cmds
|
local tokens cmd prefix trigger tail matches lbuf d_cmds
|
||||||
setopt localoptions noshwordsplit noksh_arrays noposixbuiltins
|
setopt localoptions noshwordsplit noksh_arrays noposixbuiltins
|
||||||
|
|
||||||
# http://zsh.sourceforge.net/FAQ/zshfaq03.html
|
# http://zsh.sourceforge.net/FAQ/zshfaq03.html
|
||||||
@@ -152,7 +272,7 @@ fzf-completion() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cmd=${tokens[1]}
|
cmd=$(__fzf_extract_command "$LBUFFER")
|
||||||
|
|
||||||
# Explicitly allow for empty trigger.
|
# Explicitly allow for empty trigger.
|
||||||
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
||||||
@@ -164,24 +284,24 @@ fzf-completion() {
|
|||||||
tokens=(${tokens[0,-2]})
|
tokens=(${tokens[0,-2]})
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
lbuf=$LBUFFER
|
||||||
tail=${LBUFFER:$(( ${#LBUFFER} - ${#trigger} ))}
|
tail=${LBUFFER:$(( ${#LBUFFER} - ${#trigger} ))}
|
||||||
# Kill completion (do not require trigger sequence)
|
# Kill completion (do not require trigger sequence)
|
||||||
if [ $cmd = kill -a ${LBUFFER[-1]} = ' ' ]; then
|
if [ "$cmd" = kill -a ${LBUFFER[-1]} = ' ' ]; then
|
||||||
fzf="$(__fzfcmd_complete)"
|
tail=$trigger
|
||||||
matches=$(command ps -ef | sed 1d | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-50%} --min-height 15 --reverse $FZF_DEFAULT_OPTS --preview 'echo {}' --preview-window down:3:wrap $FZF_COMPLETION_OPTS" ${=fzf} -m | awk '{print $2}' | tr '\n' ' ')
|
tokens+=$trigger
|
||||||
if [ -n "$matches" ]; then
|
lbuf="$lbuf$trigger"
|
||||||
LBUFFER="$LBUFFER$matches"
|
|
||||||
fi
|
fi
|
||||||
zle reset-prompt
|
|
||||||
# Trigger sequence given
|
# Trigger sequence given
|
||||||
elif [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
|
if [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
|
||||||
d_cmds=(${=FZF_COMPLETION_DIR_COMMANDS:-cd pushd rmdir})
|
d_cmds=(${=FZF_COMPLETION_DIR_COMMANDS:-cd pushd rmdir})
|
||||||
|
|
||||||
[ -z "$trigger" ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}}
|
[ -z "$trigger" ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}}
|
||||||
[ -z "${tokens[-1]}" ] && lbuf=$LBUFFER || lbuf=${LBUFFER:0:-${#tokens[-1]}}
|
[ -n "${tokens[-1]}" ] && lbuf=${lbuf:0:-${#tokens[-1]}}
|
||||||
|
|
||||||
if eval "type _fzf_complete_${cmd} > /dev/null"; then
|
if eval "type _fzf_complete_${cmd} > /dev/null"; then
|
||||||
eval "prefix=\"$prefix\" _fzf_complete_${cmd} \"$lbuf\""
|
prefix="$prefix" eval _fzf_complete_${cmd} ${(q)lbuf}
|
||||||
elif [ ${d_cmds[(i)$cmd]} -le ${#d_cmds} ]; then
|
elif [ ${d_cmds[(i)$cmd]} -le ${#d_cmds} ]; then
|
||||||
_fzf_dir_completion "$prefix" "$lbuf"
|
_fzf_dir_completion "$prefix" "$lbuf"
|
||||||
else
|
else
|
||||||
@@ -202,4 +322,8 @@ fzf-completion() {
|
|||||||
zle -N fzf-completion
|
zle -N fzf-completion
|
||||||
bindkey '^I' fzf-completion
|
bindkey '^I' fzf-completion
|
||||||
|
|
||||||
fi
|
} always {
|
||||||
|
# Restore the original options.
|
||||||
|
eval $__fzf_completion_options
|
||||||
|
'unset' '__fzf_completion_options'
|
||||||
|
}
|
||||||
|
@@ -1,3 +1,16 @@
|
|||||||
|
# ____ ____
|
||||||
|
# / __/___ / __/
|
||||||
|
# / /_/_ / / /_
|
||||||
|
# / __/ / /_/ __/
|
||||||
|
# /_/ /___/_/ key-bindings.bash
|
||||||
|
#
|
||||||
|
# - $FZF_TMUX_OPTS
|
||||||
|
# - $FZF_CTRL_T_COMMAND
|
||||||
|
# - $FZF_CTRL_T_OPTS
|
||||||
|
# - $FZF_CTRL_R_OPTS
|
||||||
|
# - $FZF_ALT_C_COMMAND
|
||||||
|
# - $FZF_ALT_C_OPTS
|
||||||
|
|
||||||
# Key bindings
|
# Key bindings
|
||||||
# ------------
|
# ------------
|
||||||
__fzf_select__() {
|
__fzf_select__() {
|
||||||
@@ -5,7 +18,7 @@ __fzf_select__() {
|
|||||||
-o -type f -print \
|
-o -type f -print \
|
||||||
-o -type d -print \
|
-o -type d -print \
|
||||||
-o -type l -print 2> /dev/null | cut -b3-"}"
|
-o -type l -print 2> /dev/null | cut -b3-"}"
|
||||||
eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS" fzf -m "$@" | while read -r item; do
|
eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS" $(__fzfcmd) -m "$@" | while read -r item; do
|
||||||
printf '%q ' "$item"
|
printf '%q ' "$item"
|
||||||
done
|
done
|
||||||
echo
|
echo
|
||||||
@@ -13,35 +26,15 @@ __fzf_select__() {
|
|||||||
|
|
||||||
if [[ $- =~ i ]]; then
|
if [[ $- =~ i ]]; then
|
||||||
|
|
||||||
__fzf_use_tmux__() {
|
|
||||||
[ -n "$TMUX_PANE" ] && [ "${FZF_TMUX:-0}" != 0 ] && [ ${LINES:-40} -gt 15 ]
|
|
||||||
}
|
|
||||||
|
|
||||||
__fzfcmd() {
|
__fzfcmd() {
|
||||||
__fzf_use_tmux__ &&
|
[ -n "$TMUX_PANE" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "$FZF_TMUX_OPTS" ]; } &&
|
||||||
echo "fzf-tmux -d${FZF_TMUX_HEIGHT:-40%}" || echo "fzf"
|
echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
|
||||||
}
|
|
||||||
|
|
||||||
__fzf_select_tmux__() {
|
|
||||||
local height
|
|
||||||
height=${FZF_TMUX_HEIGHT:-40%}
|
|
||||||
if [[ $height =~ %$ ]]; then
|
|
||||||
height="-p ${height%\%}"
|
|
||||||
else
|
|
||||||
height="-l $height"
|
|
||||||
fi
|
|
||||||
|
|
||||||
tmux split-window $height "cd $(printf %q "$PWD"); FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS") PATH=$(printf %q "$PATH") FZF_CTRL_T_COMMAND=$(printf %q "$FZF_CTRL_T_COMMAND") FZF_CTRL_T_OPTS=$(printf %q "$FZF_CTRL_T_OPTS") bash -c 'source \"${BASH_SOURCE[0]}\"; RESULT=\"\$(__fzf_select__ --no-height)\"; tmux setb -b fzf \"\$RESULT\" \\; pasteb -b fzf -t $TMUX_PANE \\; deleteb -b fzf || tmux send-keys -t $TMUX_PANE \"\$RESULT\"'"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fzf-file-widget() {
|
fzf-file-widget() {
|
||||||
if __fzf_use_tmux__; then
|
|
||||||
__fzf_select_tmux__
|
|
||||||
else
|
|
||||||
local selected="$(__fzf_select__)"
|
local selected="$(__fzf_select__)"
|
||||||
READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
|
READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
|
||||||
READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
|
READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__fzf_cd__() {
|
__fzf_cd__() {
|
||||||
@@ -51,71 +44,53 @@ __fzf_cd__() {
|
|||||||
dir=$(eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS" $(__fzfcmd) +m) && printf 'cd %q' "$dir"
|
dir=$(eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS" $(__fzfcmd) +m) && printf 'cd %q' "$dir"
|
||||||
}
|
}
|
||||||
|
|
||||||
__fzf_history__() (
|
__fzf_history__() {
|
||||||
local line
|
local output
|
||||||
shopt -u nocaseglob nocasematch
|
output=$(
|
||||||
line=$(
|
builtin fc -lnr -2147483648 |
|
||||||
HISTTIMEFORMAT= builtin history |
|
last_hist=$(HISTTIMEFORMAT='' builtin history 1) perl -n -l0 -e 'BEGIN { getc; $/ = "\n\t"; $HISTCMD = $ENV{last_hist} + 1 } s/^[ *]//; print $HISTCMD - $. . "\t$_" if !$seen{$_}++' |
|
||||||
FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS --tac --sync -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS +m" $(__fzfcmd) |
|
FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS +m --read0" $(__fzfcmd) --query "$READLINE_LINE"
|
||||||
command grep '^ *[0-9]') &&
|
) || return
|
||||||
if [[ $- =~ H ]]; then
|
READLINE_LINE=${output#*$'\t'}
|
||||||
sed 's/^ *\([0-9]*\)\** .*/!\1/' <<< "$line"
|
if [ -z "$READLINE_POINT" ]; then
|
||||||
|
echo "$READLINE_LINE"
|
||||||
else
|
else
|
||||||
sed 's/^ *\([0-9]*\)\** *//' <<< "$line"
|
READLINE_POINT=0x7fffffff
|
||||||
fi
|
fi
|
||||||
)
|
}
|
||||||
|
|
||||||
if [[ ! -o vi ]]; then
|
|
||||||
# Required to refresh the prompt after fzf
|
# Required to refresh the prompt after fzf
|
||||||
bind '"\er": redraw-current-line'
|
bind -m emacs-standard '"\er": redraw-current-line'
|
||||||
bind '"\e^": history-expand-line'
|
|
||||||
|
|
||||||
|
bind -m vi-command '"\C-z": emacs-editing-mode'
|
||||||
|
bind -m vi-insert '"\C-z": emacs-editing-mode'
|
||||||
|
bind -m emacs-standard '"\C-z": vi-editing-mode'
|
||||||
|
|
||||||
|
if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then
|
||||||
# CTRL-T - Paste the selected file path into the command line
|
# CTRL-T - Paste the selected file path into the command line
|
||||||
if [ $BASH_VERSINFO -gt 3 ]; then
|
bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"'
|
||||||
bind -x '"\C-t": "fzf-file-widget"'
|
bind -m vi-command '"\C-t": "\C-z\C-t\C-z"'
|
||||||
elif __fzf_use_tmux__; then
|
bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"'
|
||||||
bind '"\C-t": " \C-u \C-a\C-k`__fzf_select_tmux__`\e\C-e\C-y\C-a\C-d\C-y\ey\C-h"'
|
|
||||||
else
|
|
||||||
bind '"\C-t": " \C-u \C-a\C-k`__fzf_select__`\e\C-e\C-y\C-a\C-y\ey\C-h\C-e\er \C-h"'
|
|
||||||
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 '"\C-r": " \C-e\C-u\C-y\ey\C-u`__fzf_history__`\e\C-e\er\e^"'
|
bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u"$(__fzf_history__)"\e\C-e\er"'
|
||||||
|
bind -m vi-command '"\C-r": "\C-z\C-r\C-z"'
|
||||||
# ALT-C - cd into the selected directory
|
bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
|
||||||
bind '"\ec": " \C-e\C-u`__fzf_cd__`\e\C-e\er\C-m"'
|
|
||||||
else
|
else
|
||||||
# We'd usually use "\e" to enter vi-movement-mode so we can do our magic,
|
|
||||||
# but this incurs a very noticeable delay of a half second or so,
|
|
||||||
# because many other commands start with "\e".
|
|
||||||
# Instead, we bind an unused key, "\C-x\C-a",
|
|
||||||
# to also enter vi-movement-mode,
|
|
||||||
# and then use that thereafter.
|
|
||||||
# (We imagine that "\C-x\C-a" is relatively unlikely to be in use.)
|
|
||||||
bind '"\C-x\C-a": vi-movement-mode'
|
|
||||||
|
|
||||||
bind '"\C-x\C-e": shell-expand-line'
|
|
||||||
bind '"\C-x\C-r": redraw-current-line'
|
|
||||||
bind '"\C-x^": history-expand-line'
|
|
||||||
|
|
||||||
# CTRL-T - Paste the selected file path into the command line
|
# CTRL-T - Paste the selected file path into the command line
|
||||||
# - FIXME: Selected items are attached to the end regardless of cursor position
|
bind -m emacs-standard -x '"\C-t": fzf-file-widget'
|
||||||
if [ $BASH_VERSINFO -gt 3 ]; then
|
bind -m vi-command -x '"\C-t": fzf-file-widget'
|
||||||
bind -x '"\C-t": "fzf-file-widget"'
|
bind -m vi-insert -x '"\C-t": fzf-file-widget'
|
||||||
elif __fzf_use_tmux__; then
|
|
||||||
bind '"\C-t": "\C-x\C-a$a \C-x\C-addi`__fzf_select_tmux__`\C-x\C-e\C-x\C-a0P$xa"'
|
|
||||||
else
|
|
||||||
bind '"\C-t": "\C-x\C-a$a \C-x\C-addi`__fzf_select__`\C-x\C-e\C-x\C-a0Px$a \C-x\C-r\C-x\C-axa "'
|
|
||||||
fi
|
|
||||||
bind -m vi-command '"\C-t": "i\C-t"'
|
|
||||||
|
|
||||||
# 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 '"\C-r": "\C-x\C-addi`__fzf_history__`\C-x\C-e\C-x\C-r\C-x^\C-x\C-a$a"'
|
bind -m emacs-standard -x '"\C-r": __fzf_history__'
|
||||||
bind -m vi-command '"\C-r": "i\C-r"'
|
bind -m vi-command -x '"\C-r": __fzf_history__'
|
||||||
|
bind -m vi-insert -x '"\C-r": __fzf_history__'
|
||||||
|
fi
|
||||||
|
|
||||||
# ALT-C - cd into the selected directory
|
# ALT-C - cd into the selected directory
|
||||||
bind '"\ec": "\C-x\C-addi`__fzf_cd__`\C-x\C-e\C-x\C-r\C-m"'
|
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-command '"\ec": "ddi`__fzf_cd__`\C-x\C-e\C-x\C-r\C-m"'
|
bind -m vi-command '"\ec": "\C-z\ec\C-z"'
|
||||||
fi
|
bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
@@ -1,3 +1,16 @@
|
|||||||
|
# ____ ____
|
||||||
|
# / __/___ / __/
|
||||||
|
# / /_/_ / / /_
|
||||||
|
# / __/ / /_/ __/
|
||||||
|
# /_/ /___/_/ key-bindings.fish
|
||||||
|
#
|
||||||
|
# - $FZF_TMUX_OPTS
|
||||||
|
# - $FZF_CTRL_T_COMMAND
|
||||||
|
# - $FZF_CTRL_T_OPTS
|
||||||
|
# - $FZF_CTRL_R_OPTS
|
||||||
|
# - $FZF_ALT_C_COMMAND
|
||||||
|
# - $FZF_ALT_C_OPTS
|
||||||
|
|
||||||
# Key bindings
|
# Key bindings
|
||||||
# ------------
|
# ------------
|
||||||
function fzf_key_bindings
|
function fzf_key_bindings
|
||||||
@@ -10,13 +23,13 @@ function fzf_key_bindings
|
|||||||
|
|
||||||
# "-path \$dir'*/\\.*'" matches hidden files/folders inside $dir but not
|
# "-path \$dir'*/\\.*'" matches hidden files/folders inside $dir but not
|
||||||
# $dir itself, even if hidden.
|
# $dir itself, even if hidden.
|
||||||
set -q FZF_CTRL_T_COMMAND; or set -l FZF_CTRL_T_COMMAND "
|
test -n "$FZF_CTRL_T_COMMAND"; or set -l FZF_CTRL_T_COMMAND "
|
||||||
command find -L \$dir -mindepth 1 \\( -path \$dir'*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \
|
command find -L \$dir -mindepth 1 \\( -path \$dir'*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \
|
||||||
-o -type f -print \
|
-o -type f -print \
|
||||||
-o -type d -print \
|
-o -type d -print \
|
||||||
-o -type l -print 2> /dev/null | sed 's@^\./@@'"
|
-o -type l -print 2> /dev/null | sed 's@^\./@@'"
|
||||||
|
|
||||||
set -q 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 $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS"
|
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS"
|
||||||
eval "$FZF_CTRL_T_COMMAND | "(__fzfcmd)' -m --query "'$fzf_query'"' | while read -l r; set result $result $r; end
|
eval "$FZF_CTRL_T_COMMAND | "(__fzfcmd)' -m --query "'$fzf_query'"' | while read -l r; set result $result $r; end
|
||||||
@@ -36,7 +49,7 @@ function fzf_key_bindings
|
|||||||
end
|
end
|
||||||
|
|
||||||
function fzf-history-widget -d "Show command history"
|
function fzf-history-widget -d "Show command history"
|
||||||
set -q 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 $FZF_DEFAULT_OPTS --tiebreak=index --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS +m"
|
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT $FZF_DEFAULT_OPTS --tiebreak=index --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS +m"
|
||||||
|
|
||||||
@@ -62,10 +75,10 @@ function fzf_key_bindings
|
|||||||
set -l dir $commandline[1]
|
set -l dir $commandline[1]
|
||||||
set -l fzf_query $commandline[2]
|
set -l fzf_query $commandline[2]
|
||||||
|
|
||||||
set -q FZF_ALT_C_COMMAND; or set -l FZF_ALT_C_COMMAND "
|
test -n "$FZF_ALT_C_COMMAND"; or set -l FZF_ALT_C_COMMAND "
|
||||||
command find -L \$dir -mindepth 1 \\( -path \$dir'*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \
|
command find -L \$dir -mindepth 1 \\( -path \$dir'*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \
|
||||||
-o -type d -print 2> /dev/null | sed 's@^\./@@'"
|
-o -type d -print 2> /dev/null | sed 's@^\./@@'"
|
||||||
set -q 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 $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS"
|
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS"
|
||||||
eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)' +m --query "'$fzf_query'"' | read -l result
|
eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)' +m --query "'$fzf_query'"' | read -l result
|
||||||
@@ -82,10 +95,12 @@ function fzf_key_bindings
|
|||||||
end
|
end
|
||||||
|
|
||||||
function __fzfcmd
|
function __fzfcmd
|
||||||
set -q FZF_TMUX; or set FZF_TMUX 0
|
test -n "$FZF_TMUX"; or set FZF_TMUX 0
|
||||||
set -q FZF_TMUX_HEIGHT; or set FZF_TMUX_HEIGHT 40%
|
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
||||||
if [ $FZF_TMUX -eq 1 ]
|
if [ -n "$FZF_TMUX_OPTS" ]
|
||||||
echo "fzf-tmux -d$FZF_TMUX_HEIGHT"
|
echo "fzf-tmux $FZF_TMUX_OPTS -- "
|
||||||
|
else if [ $FZF_TMUX -eq 1 ]
|
||||||
|
echo "fzf-tmux -d$FZF_TMUX_HEIGHT -- "
|
||||||
else
|
else
|
||||||
echo "fzf"
|
echo "fzf"
|
||||||
end
|
end
|
||||||
|
@@ -1,6 +1,42 @@
|
|||||||
|
# ____ ____
|
||||||
|
# / __/___ / __/
|
||||||
|
# / /_/_ / / /_
|
||||||
|
# / __/ / /_/ __/
|
||||||
|
# /_/ /___/_/ key-bindings.zsh
|
||||||
|
#
|
||||||
|
# - $FZF_TMUX_OPTS
|
||||||
|
# - $FZF_CTRL_T_COMMAND
|
||||||
|
# - $FZF_CTRL_T_OPTS
|
||||||
|
# - $FZF_CTRL_R_OPTS
|
||||||
|
# - $FZF_ALT_C_COMMAND
|
||||||
|
# - $FZF_ALT_C_OPTS
|
||||||
|
|
||||||
# Key bindings
|
# Key bindings
|
||||||
# ------------
|
# ------------
|
||||||
if [[ $- == *i* ]]; then
|
|
||||||
|
# The code at the top and the bottom of this file is the same as in completion.zsh.
|
||||||
|
# Refer to that file for explanation.
|
||||||
|
if 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then
|
||||||
|
__fzf_key_bindings_options="options=(${(j: :)${(kv)options[@]}})"
|
||||||
|
else
|
||||||
|
() {
|
||||||
|
__fzf_key_bindings_options="setopt"
|
||||||
|
'local' '__fzf_opt'
|
||||||
|
for __fzf_opt in "${(@)${(@f)$(set -o)}%% *}"; do
|
||||||
|
if [[ -o "$__fzf_opt" ]]; then
|
||||||
|
__fzf_key_bindings_options+=" -o $__fzf_opt"
|
||||||
|
else
|
||||||
|
__fzf_key_bindings_options+=" +o $__fzf_opt"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
'emulate' 'zsh' '-o' 'no_aliases'
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
[[ -o interactive ]] || return 0
|
||||||
|
|
||||||
# CTRL-T - Paste the selected file path(s) into the command line
|
# CTRL-T - Paste the selected file path(s) into the command line
|
||||||
__fsel() {
|
__fsel() {
|
||||||
@@ -17,13 +53,9 @@ __fsel() {
|
|||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
|
|
||||||
__fzf_use_tmux__() {
|
|
||||||
[ -n "$TMUX_PANE" ] && [ "${FZF_TMUX:-0}" != 0 ] && [ ${LINES:-40} -gt 15 ]
|
|
||||||
}
|
|
||||||
|
|
||||||
__fzfcmd() {
|
__fzfcmd() {
|
||||||
__fzf_use_tmux__ &&
|
[ -n "$TMUX_PANE" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "$FZF_TMUX_OPTS" ]; } &&
|
||||||
echo "fzf-tmux -d${FZF_TMUX_HEIGHT:-40%}" || echo "fzf"
|
echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
|
||||||
}
|
}
|
||||||
|
|
||||||
fzf-file-widget() {
|
fzf-file-widget() {
|
||||||
@@ -55,9 +87,15 @@ fzf-cd-widget() {
|
|||||||
zle redisplay
|
zle redisplay
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
if [ -z "$BUFFER" ]; then
|
||||||
|
BUFFER="cd ${(q)dir}"
|
||||||
|
zle accept-line
|
||||||
|
else
|
||||||
|
print -sr "cd ${(q)dir}"
|
||||||
cd "$dir"
|
cd "$dir"
|
||||||
unset dir # ensure this doesn't end up appearing in prompt expansion
|
fi
|
||||||
local ret=$?
|
local ret=$?
|
||||||
|
unset dir # ensure this doesn't end up appearing in prompt expansion
|
||||||
zle fzf-redraw-prompt
|
zle fzf-redraw-prompt
|
||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
@@ -68,7 +106,7 @@ bindkey '\ec' fzf-cd-widget
|
|||||||
fzf-history-widget() {
|
fzf-history-widget() {
|
||||||
local selected num
|
local selected num
|
||||||
setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
|
setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
|
||||||
selected=( $(fc -rl 1 |
|
selected=( $(fc -rl 1 | perl -ne 'print if !$seen{(/^\s*[0-9]+\**\s+(.*)/, $1)}++' |
|
||||||
FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
|
FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
|
||||||
local ret=$?
|
local ret=$?
|
||||||
if [ -n "$selected" ]; then
|
if [ -n "$selected" ]; then
|
||||||
@@ -83,4 +121,7 @@ fzf-history-widget() {
|
|||||||
zle -N fzf-history-widget
|
zle -N fzf-history-widget
|
||||||
bindkey '^R' fzf-history-widget
|
bindkey '^R' fzf-history-widget
|
||||||
|
|
||||||
fi
|
} always {
|
||||||
|
eval $__fzf_key_bindings_options
|
||||||
|
'unset' '__fzf_key_bindings_options'
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2017 Junegunn Choi
|
Copyright (c) 2013-2020 Junegunn Choi
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@@ -773,12 +773,17 @@ func PrefixMatch(caseSensitive bool, normalize bool, forward bool, text *util.Ch
|
|||||||
return Result{0, 0, 0}, nil
|
return Result{0, 0, 0}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if text.Length() < len(pattern) {
|
trimmedLen := 0
|
||||||
|
if !unicode.IsSpace(pattern[0]) {
|
||||||
|
trimmedLen = text.LeadingWhitespaces()
|
||||||
|
}
|
||||||
|
|
||||||
|
if text.Length()-trimmedLen < len(pattern) {
|
||||||
return Result{-1, -1, 0}, nil
|
return Result{-1, -1, 0}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for index, r := range pattern {
|
for index, r := range pattern {
|
||||||
char := text.Get(index)
|
char := text.Get(trimmedLen + index)
|
||||||
if !caseSensitive {
|
if !caseSensitive {
|
||||||
char = unicode.ToLower(char)
|
char = unicode.ToLower(char)
|
||||||
}
|
}
|
||||||
@@ -790,14 +795,17 @@ func PrefixMatch(caseSensitive bool, normalize bool, forward bool, text *util.Ch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
lenPattern := len(pattern)
|
lenPattern := len(pattern)
|
||||||
score, _ := calculateScore(caseSensitive, normalize, text, pattern, 0, lenPattern, false)
|
score, _ := calculateScore(caseSensitive, normalize, text, pattern, trimmedLen, trimmedLen+lenPattern, false)
|
||||||
return Result{0, lenPattern, score}, nil
|
return Result{trimmedLen, trimmedLen + lenPattern, score}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SuffixMatch performs suffix-match
|
// SuffixMatch performs suffix-match
|
||||||
func SuffixMatch(caseSensitive bool, normalize bool, forward bool, text *util.Chars, pattern []rune, withPos bool, slab *util.Slab) (Result, *[]int) {
|
func SuffixMatch(caseSensitive bool, normalize bool, forward bool, text *util.Chars, pattern []rune, withPos bool, slab *util.Slab) (Result, *[]int) {
|
||||||
lenRunes := text.Length()
|
lenRunes := text.Length()
|
||||||
trimmedLen := lenRunes - text.TrailingWhitespaces()
|
trimmedLen := lenRunes
|
||||||
|
if len(pattern) == 0 || !unicode.IsSpace(pattern[len(pattern)-1]) {
|
||||||
|
trimmedLen -= text.TrailingWhitespaces()
|
||||||
|
}
|
||||||
if len(pattern) == 0 {
|
if len(pattern) == 0 {
|
||||||
return Result{trimmedLen, trimmedLen, 0}, nil
|
return Result{trimmedLen, trimmedLen, 0}, nil
|
||||||
}
|
}
|
||||||
@@ -828,14 +836,30 @@ func SuffixMatch(caseSensitive bool, normalize bool, forward bool, text *util.Ch
|
|||||||
// EqualMatch performs equal-match
|
// EqualMatch performs equal-match
|
||||||
func EqualMatch(caseSensitive bool, normalize bool, forward bool, text *util.Chars, pattern []rune, withPos bool, slab *util.Slab) (Result, *[]int) {
|
func EqualMatch(caseSensitive bool, normalize bool, forward bool, text *util.Chars, pattern []rune, withPos bool, slab *util.Slab) (Result, *[]int) {
|
||||||
lenPattern := len(pattern)
|
lenPattern := len(pattern)
|
||||||
if text.Length() != lenPattern {
|
if lenPattern == 0 {
|
||||||
|
return Result{-1, -1, 0}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip leading whitespaces
|
||||||
|
trimmedLen := 0
|
||||||
|
if !unicode.IsSpace(pattern[0]) {
|
||||||
|
trimmedLen = text.LeadingWhitespaces()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip trailing whitespaces
|
||||||
|
trimmedEndLen := 0
|
||||||
|
if !unicode.IsSpace(pattern[lenPattern-1]) {
|
||||||
|
trimmedEndLen = text.TrailingWhitespaces()
|
||||||
|
}
|
||||||
|
|
||||||
|
if text.Length()-trimmedLen-trimmedEndLen != lenPattern {
|
||||||
return Result{-1, -1, 0}, nil
|
return Result{-1, -1, 0}, nil
|
||||||
}
|
}
|
||||||
match := true
|
match := true
|
||||||
if normalize {
|
if normalize {
|
||||||
runes := text.ToRunes()
|
runes := text.ToRunes()
|
||||||
for idx, pchar := range pattern {
|
for idx, pchar := range pattern {
|
||||||
char := runes[idx]
|
char := runes[trimmedLen+idx]
|
||||||
if !caseSensitive {
|
if !caseSensitive {
|
||||||
char = unicode.To(unicode.LowerCase, char)
|
char = unicode.To(unicode.LowerCase, char)
|
||||||
}
|
}
|
||||||
@@ -845,14 +869,15 @@ func EqualMatch(caseSensitive bool, normalize bool, forward bool, text *util.Cha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
runesStr := text.ToString()
|
runes := text.ToRunes()
|
||||||
|
runesStr := string(runes[trimmedLen : len(runes)-trimmedEndLen])
|
||||||
if !caseSensitive {
|
if !caseSensitive {
|
||||||
runesStr = strings.ToLower(runesStr)
|
runesStr = strings.ToLower(runesStr)
|
||||||
}
|
}
|
||||||
match = runesStr == string(pattern)
|
match = runesStr == string(pattern)
|
||||||
}
|
}
|
||||||
if match {
|
if match {
|
||||||
return Result{0, lenPattern, (scoreMatch+bonusBoundary)*lenPattern +
|
return Result{trimmedLen, trimmedLen + lenPattern, (scoreMatch+bonusBoundary)*lenPattern +
|
||||||
(bonusFirstCharMultiplier-1)*bonusBoundary}, nil
|
(bonusFirstCharMultiplier-1)*bonusBoundary}, nil
|
||||||
}
|
}
|
||||||
return Result{-1, -1, 0}, nil
|
return Result{-1, -1, 0}, nil
|
||||||
|
@@ -136,6 +136,10 @@ func TestPrefixMatch(t *testing.T) {
|
|||||||
assertMatch(t, PrefixMatch, false, dir, "fooBarbaz", "Foo", 0, 3, score)
|
assertMatch(t, PrefixMatch, false, dir, "fooBarbaz", "Foo", 0, 3, score)
|
||||||
assertMatch(t, PrefixMatch, false, dir, "foOBarBaZ", "foo", 0, 3, score)
|
assertMatch(t, PrefixMatch, false, dir, "foOBarBaZ", "foo", 0, 3, score)
|
||||||
assertMatch(t, PrefixMatch, false, dir, "f-oBarbaz", "f-o", 0, 3, score)
|
assertMatch(t, PrefixMatch, false, dir, "f-oBarbaz", "f-o", 0, 3, score)
|
||||||
|
|
||||||
|
assertMatch(t, PrefixMatch, false, dir, " fooBar", "foo", 1, 4, score)
|
||||||
|
assertMatch(t, PrefixMatch, false, dir, " fooBar", " fo", 0, 3, score)
|
||||||
|
assertMatch(t, PrefixMatch, false, dir, " fo", "foo", -1, -1, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +152,13 @@ func TestSuffixMatch(t *testing.T) {
|
|||||||
scoreMatch*3+bonusConsecutive*2)
|
scoreMatch*3+bonusConsecutive*2)
|
||||||
assertMatch(t, SuffixMatch, false, dir, "fooBarBaZ", "baz", 6, 9,
|
assertMatch(t, SuffixMatch, false, dir, "fooBarBaZ", "baz", 6, 9,
|
||||||
(scoreMatch+bonusCamel123)*3+bonusCamel123*(bonusFirstCharMultiplier-1))
|
(scoreMatch+bonusCamel123)*3+bonusCamel123*(bonusFirstCharMultiplier-1))
|
||||||
|
|
||||||
|
// Strip trailing white space from the string
|
||||||
|
assertMatch(t, SuffixMatch, false, dir, "fooBarbaz ", "baz", 6, 9,
|
||||||
|
scoreMatch*3+bonusConsecutive*2)
|
||||||
|
// Only when the pattern doesn't end with a space
|
||||||
|
assertMatch(t, SuffixMatch, false, dir, "fooBarbaz ", "baz ", 6, 10,
|
||||||
|
scoreMatch*4+bonusConsecutive*2+bonusNonWord)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -405,6 +405,74 @@ var normalized map[rune]rune = map[rune]rune{
|
|||||||
0x024E: 'Y', // WITH STROKE, LATIN CAPITAL LETTER
|
0x024E: 'Y', // WITH STROKE, LATIN CAPITAL LETTER
|
||||||
0x028F: 'Y', // , LATIN LETTER SMALL CAPITAL
|
0x028F: 'Y', // , LATIN LETTER SMALL CAPITAL
|
||||||
0x1D22: 'Z', // , LATIN LETTER SMALL CAPITAL
|
0x1D22: 'Z', // , LATIN LETTER SMALL CAPITAL
|
||||||
|
|
||||||
|
'Ắ': 'A',
|
||||||
|
'Ấ': 'A',
|
||||||
|
'Ằ': 'A',
|
||||||
|
'Ầ': 'A',
|
||||||
|
'Ẳ': 'A',
|
||||||
|
'Ẩ': 'A',
|
||||||
|
'Ẵ': 'A',
|
||||||
|
'Ẫ': 'A',
|
||||||
|
'Ặ': 'A',
|
||||||
|
'Ậ': 'A',
|
||||||
|
|
||||||
|
'ắ': 'a',
|
||||||
|
'ấ': 'a',
|
||||||
|
'ằ': 'a',
|
||||||
|
'ầ': 'a',
|
||||||
|
'ẳ': 'a',
|
||||||
|
'ẩ': 'a',
|
||||||
|
'ẵ': 'a',
|
||||||
|
'ẫ': 'a',
|
||||||
|
'ặ': 'a',
|
||||||
|
'ậ': 'a',
|
||||||
|
|
||||||
|
'Ế': 'E',
|
||||||
|
'Ề': 'E',
|
||||||
|
'Ể': 'E',
|
||||||
|
'Ễ': 'E',
|
||||||
|
'Ệ': 'E',
|
||||||
|
|
||||||
|
'ế': 'e',
|
||||||
|
'ề': 'e',
|
||||||
|
'ể': 'e',
|
||||||
|
'ễ': 'e',
|
||||||
|
'ệ': 'e',
|
||||||
|
|
||||||
|
'Ố': 'O',
|
||||||
|
'Ớ': 'O',
|
||||||
|
'Ồ': 'O',
|
||||||
|
'Ờ': 'O',
|
||||||
|
'Ổ': 'O',
|
||||||
|
'Ở': 'O',
|
||||||
|
'Ỗ': 'O',
|
||||||
|
'Ỡ': 'O',
|
||||||
|
'Ộ': 'O',
|
||||||
|
'Ợ': 'O',
|
||||||
|
|
||||||
|
'ố': 'o',
|
||||||
|
'ớ': 'o',
|
||||||
|
'ồ': 'o',
|
||||||
|
'ờ': 'o',
|
||||||
|
'ổ': 'o',
|
||||||
|
'ở': 'o',
|
||||||
|
'ỗ': 'o',
|
||||||
|
'ỡ': 'o',
|
||||||
|
'ộ': 'o',
|
||||||
|
'ợ': 'o',
|
||||||
|
|
||||||
|
'Ứ': 'U',
|
||||||
|
'Ừ': 'U',
|
||||||
|
'Ử': 'U',
|
||||||
|
'Ữ': 'U',
|
||||||
|
'Ự': 'U',
|
||||||
|
|
||||||
|
'ứ': 'u',
|
||||||
|
'ừ': 'u',
|
||||||
|
'ử': 'u',
|
||||||
|
'ữ': 'u',
|
||||||
|
'ự': 'u',
|
||||||
}
|
}
|
||||||
|
|
||||||
// NormalizeRunes normalizes latin script letters
|
// NormalizeRunes normalizes latin script letters
|
||||||
|
@@ -90,10 +90,11 @@ func init() {
|
|||||||
- http://ascii-table.com/ansi-escape-sequences.php
|
- http://ascii-table.com/ansi-escape-sequences.php
|
||||||
- http://ascii-table.com/ansi-escape-sequences-vt-100.php
|
- http://ascii-table.com/ansi-escape-sequences-vt-100.php
|
||||||
- http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html
|
- http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html
|
||||||
|
- https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||||||
*/
|
*/
|
||||||
// The following regular expression will include not all but most of the
|
// The following regular expression will include not all but most of the
|
||||||
// frequently used ANSI sequences
|
// frequently used ANSI sequences
|
||||||
ansiRegex = regexp.MustCompile("(?:\x1b[\\[()][0-9;]*[a-zA-Z@]|\x1b.|[\x0e\x0f]|.\x08)")
|
ansiRegex = regexp.MustCompile("(?:\x1b[\\[()][0-9;]*[a-zA-Z@]|\x1b][0-9];[[:print:]]+(?:\x1b\\\\|\x07)|\x1b.|[\x0e\x0f]|.\x08)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func findAnsiStart(str string) int {
|
func findAnsiStart(str string) int {
|
||||||
@@ -243,6 +244,10 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
|
|||||||
state.attr = state.attr | tui.Blink
|
state.attr = state.attr | tui.Blink
|
||||||
case 7:
|
case 7:
|
||||||
state.attr = state.attr | tui.Reverse
|
state.attr = state.attr | tui.Reverse
|
||||||
|
case 23: // tput rmso
|
||||||
|
state.attr = state.attr &^ tui.Italic
|
||||||
|
case 24: // tput rmul
|
||||||
|
state.attr = state.attr &^ tui.Underline
|
||||||
case 0:
|
case 0:
|
||||||
init()
|
init()
|
||||||
default:
|
default:
|
||||||
|
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Current version
|
// Current version
|
||||||
version = "0.20.0"
|
version = "0.23.1"
|
||||||
|
|
||||||
// Core
|
// Core
|
||||||
coordinatorDelayMax time.Duration = 100 * time.Millisecond
|
coordinatorDelayMax time.Duration = 100 * time.Millisecond
|
||||||
@@ -25,7 +25,7 @@ const (
|
|||||||
// Terminal
|
// Terminal
|
||||||
initialDelay = 20 * time.Millisecond
|
initialDelay = 20 * time.Millisecond
|
||||||
initialDelayTac = 100 * time.Millisecond
|
initialDelayTac = 100 * time.Millisecond
|
||||||
spinnerDuration = 200 * time.Millisecond
|
spinnerDuration = 100 * time.Millisecond
|
||||||
previewCancelWait = 500 * time.Millisecond
|
previewCancelWait = 500 * time.Millisecond
|
||||||
maxPatternLength = 300
|
maxPatternLength = 300
|
||||||
maxMulti = math.MaxInt32
|
maxMulti = math.MaxInt32
|
||||||
@@ -62,8 +62,6 @@ func init() {
|
|||||||
defaultCommand = `set -o pipefail; command find -L . -mindepth 1 \( -path '*/\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \) -prune -o -type f -print -o -type l -print 2> /dev/null | cut -b3-`
|
defaultCommand = `set -o pipefail; command find -L . -mindepth 1 \( -path '*/\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \) -prune -o -type f -print -o -type l -print 2> /dev/null | cut -b3-`
|
||||||
} else if os.Getenv("TERM") == "cygwin" {
|
} else if os.Getenv("TERM") == "cygwin" {
|
||||||
defaultCommand = `sh -c "command find -L . -mindepth 1 -path '*/\.*' -prune -o -type f -print -o -type l -print 2> /dev/null | cut -b3-"`
|
defaultCommand = `sh -c "command find -L . -mindepth 1 -path '*/\.*' -prune -o -type f -print -o -type l -print 2> /dev/null | cut -b3-"`
|
||||||
} else {
|
|
||||||
defaultCommand = `for /r %P in (*) do @(set "_curfile=%P" & set "_curfile=!_curfile:%__CD__%=!" & echo !_curfile!)`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
178
src/options.go
178
src/options.go
@@ -6,12 +6,13 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/junegunn/fzf/src/algo"
|
"github.com/junegunn/fzf/src/algo"
|
||||||
"github.com/junegunn/fzf/src/tui"
|
"github.com/junegunn/fzf/src/tui"
|
||||||
"github.com/junegunn/fzf/src/util"
|
|
||||||
|
|
||||||
|
"github.com/mattn/go-runewidth"
|
||||||
"github.com/mattn/go-shellwords"
|
"github.com/mattn/go-shellwords"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ const usage = `usage: fzf [options]
|
|||||||
--no-mouse Disable mouse
|
--no-mouse Disable mouse
|
||||||
--bind=KEYBINDS Custom key bindings. Refer to the man page.
|
--bind=KEYBINDS Custom key bindings. Refer to the man page.
|
||||||
--cycle Enable cyclic scroll
|
--cycle Enable cyclic scroll
|
||||||
|
--keep-right Keep the right end of the line visible on overflow
|
||||||
--no-hscroll Disable horizontal scroll
|
--no-hscroll Disable horizontal scroll
|
||||||
--hscroll-off=COL Number of screen columns to keep to the right of the
|
--hscroll-off=COL Number of screen columns to keep to the right of the
|
||||||
highlighted substring (default: 10)
|
highlighted substring (default: 10)
|
||||||
@@ -55,10 +57,13 @@ const usage = `usage: fzf [options]
|
|||||||
--min-height=HEIGHT Minimum height when --height is given in percent
|
--min-height=HEIGHT Minimum height when --height is given in percent
|
||||||
(default: 10)
|
(default: 10)
|
||||||
--layout=LAYOUT Choose layout: [default|reverse|reverse-list]
|
--layout=LAYOUT Choose layout: [default|reverse|reverse-list]
|
||||||
--border Draw border above and below the finder
|
--border[=STYLE] Draw border around the finder
|
||||||
|
[rounded|sharp|horizontal] (default: rounded)
|
||||||
--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)
|
||||||
--info=STYLE Finder info style [default|inline|hidden]
|
--info=STYLE Finder info style [default|inline|hidden]
|
||||||
--prompt=STR Input prompt (default: '> ')
|
--prompt=STR Input prompt (default: '> ')
|
||||||
|
--pointer=STR Pointer to the current line (default: '>')
|
||||||
|
--marker=STR Multi-select marker (default: '>')
|
||||||
--header=STR String to print as header
|
--header=STR String to print as header
|
||||||
--header-lines=N The first N lines of the input are treated as header
|
--header-lines=N The first N lines of the input are treated as header
|
||||||
|
|
||||||
@@ -75,7 +80,11 @@ const usage = `usage: fzf [options]
|
|||||||
Preview
|
Preview
|
||||||
--preview=COMMAND Command to preview highlighted line ({})
|
--preview=COMMAND Command to preview highlighted line ({})
|
||||||
--preview-window=OPT Preview window layout (default: right:50%)
|
--preview-window=OPT Preview window layout (default: right:50%)
|
||||||
[up|down|left|right][:SIZE[%]][:wrap][:hidden]
|
[up|down|left|right][:SIZE[%]]
|
||||||
|
[:[no]wrap][:[no]cycle][:[no]hidden]
|
||||||
|
[:rounded|sharp|noborder]
|
||||||
|
[:+SCROLL[-OFFSET]]
|
||||||
|
[:default]
|
||||||
|
|
||||||
Scripting
|
Scripting
|
||||||
-q, --query=STR Start the finder with the given query
|
-q, --query=STR Start the finder with the given query
|
||||||
@@ -154,9 +163,11 @@ type previewOpts struct {
|
|||||||
command string
|
command string
|
||||||
position windowPosition
|
position windowPosition
|
||||||
size sizeSpec
|
size sizeSpec
|
||||||
|
scroll string
|
||||||
hidden bool
|
hidden bool
|
||||||
wrap bool
|
wrap bool
|
||||||
border bool
|
cycle bool
|
||||||
|
border tui.BorderShape
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options stores the values of command-line options
|
// Options stores the values of command-line options
|
||||||
@@ -183,12 +194,15 @@ type Options struct {
|
|||||||
MinHeight int
|
MinHeight int
|
||||||
Layout layoutType
|
Layout layoutType
|
||||||
Cycle bool
|
Cycle bool
|
||||||
|
KeepRight bool
|
||||||
Hscroll bool
|
Hscroll bool
|
||||||
HscrollOff int
|
HscrollOff int
|
||||||
FileWord bool
|
FileWord bool
|
||||||
InfoStyle infoStyle
|
InfoStyle infoStyle
|
||||||
JumpLabels string
|
JumpLabels string
|
||||||
Prompt string
|
Prompt string
|
||||||
|
Pointer string
|
||||||
|
Marker string
|
||||||
Query string
|
Query string
|
||||||
Select1 bool
|
Select1 bool
|
||||||
Exit0 bool
|
Exit0 bool
|
||||||
@@ -206,13 +220,17 @@ type Options struct {
|
|||||||
Header []string
|
Header []string
|
||||||
HeaderLines int
|
HeaderLines int
|
||||||
Margin [4]sizeSpec
|
Margin [4]sizeSpec
|
||||||
Bordered bool
|
BorderShape tui.BorderShape
|
||||||
Unicode bool
|
Unicode bool
|
||||||
Tabstop int
|
Tabstop int
|
||||||
ClearOnExit bool
|
ClearOnExit bool
|
||||||
Version bool
|
Version bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func defaultPreviewOpts(command string) previewOpts {
|
||||||
|
return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, tui.BorderRounded}
|
||||||
|
}
|
||||||
|
|
||||||
func defaultOptions() *Options {
|
func defaultOptions() *Options {
|
||||||
return &Options{
|
return &Options{
|
||||||
Fuzzy: true,
|
Fuzzy: true,
|
||||||
@@ -236,12 +254,15 @@ func defaultOptions() *Options {
|
|||||||
MinHeight: 10,
|
MinHeight: 10,
|
||||||
Layout: layoutDefault,
|
Layout: layoutDefault,
|
||||||
Cycle: false,
|
Cycle: false,
|
||||||
|
KeepRight: false,
|
||||||
Hscroll: true,
|
Hscroll: true,
|
||||||
HscrollOff: 10,
|
HscrollOff: 10,
|
||||||
FileWord: false,
|
FileWord: false,
|
||||||
InfoStyle: infoDefault,
|
InfoStyle: infoDefault,
|
||||||
JumpLabels: defaultJumpLabels,
|
JumpLabels: defaultJumpLabels,
|
||||||
Prompt: "> ",
|
Prompt: "> ",
|
||||||
|
Pointer: ">",
|
||||||
|
Marker: ">",
|
||||||
Query: "",
|
Query: "",
|
||||||
Select1: false,
|
Select1: false,
|
||||||
Exit0: false,
|
Exit0: false,
|
||||||
@@ -249,7 +270,7 @@ func defaultOptions() *Options {
|
|||||||
ToggleSort: false,
|
ToggleSort: false,
|
||||||
Expect: make(map[int]string),
|
Expect: make(map[int]string),
|
||||||
Keymap: make(map[int][]action),
|
Keymap: make(map[int][]action),
|
||||||
Preview: previewOpts{"", posRight, sizeSpec{50, true}, false, false, true},
|
Preview: defaultPreviewOpts(""),
|
||||||
PrintQuery: false,
|
PrintQuery: false,
|
||||||
ReadZero: false,
|
ReadZero: false,
|
||||||
Printer: func(str string) { fmt.Println(str) },
|
Printer: func(str string) { fmt.Println(str) },
|
||||||
@@ -293,12 +314,12 @@ func nextString(args []string, i *int, message string) string {
|
|||||||
return args[*i]
|
return args[*i]
|
||||||
}
|
}
|
||||||
|
|
||||||
func optionalNextString(args []string, i *int) string {
|
func optionalNextString(args []string, i *int) (bool, string) {
|
||||||
if len(args) > *i+1 && !strings.HasPrefix(args[*i+1], "-") {
|
if len(args) > *i+1 && !strings.HasPrefix(args[*i+1], "-") && !strings.HasPrefix(args[*i+1], "+") {
|
||||||
*i++
|
*i++
|
||||||
return args[*i]
|
return true, args[*i]
|
||||||
}
|
}
|
||||||
return ""
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func atoi(str string) int {
|
func atoi(str string) int {
|
||||||
@@ -392,6 +413,23 @@ func parseAlgo(str string) algo.Algo {
|
|||||||
return algo.FuzzyMatchV2
|
return algo.FuzzyMatchV2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseBorder(str string, optional bool) tui.BorderShape {
|
||||||
|
switch str {
|
||||||
|
case "rounded":
|
||||||
|
return tui.BorderRounded
|
||||||
|
case "sharp":
|
||||||
|
return tui.BorderSharp
|
||||||
|
case "horizontal":
|
||||||
|
return tui.BorderHorizontal
|
||||||
|
default:
|
||||||
|
if optional && str == "" {
|
||||||
|
return tui.BorderRounded
|
||||||
|
}
|
||||||
|
errorExit("invalid border style (expected: rounded|sharp|horizontal)")
|
||||||
|
}
|
||||||
|
return tui.BorderNone
|
||||||
|
}
|
||||||
|
|
||||||
func parseKeyChords(str string, message string) map[int]string {
|
func parseKeyChords(str string, message string) map[int]string {
|
||||||
if len(str) == 0 {
|
if len(str) == 0 {
|
||||||
errorExit(message)
|
errorExit(message)
|
||||||
@@ -436,6 +474,8 @@ func parseKeyChords(str string, message string) map[int]string {
|
|||||||
chord = tui.CtrlRightBracket
|
chord = tui.CtrlRightBracket
|
||||||
case "change":
|
case "change":
|
||||||
chord = tui.Change
|
chord = tui.Change
|
||||||
|
case "backward-eof":
|
||||||
|
chord = tui.BackwardEOF
|
||||||
case "alt-enter", "alt-return":
|
case "alt-enter", "alt-return":
|
||||||
chord = tui.CtrlAltM
|
chord = tui.CtrlAltM
|
||||||
case "alt-space":
|
case "alt-space":
|
||||||
@@ -464,6 +504,8 @@ func parseKeyChords(str string, message string) map[int]string {
|
|||||||
chord = tui.Home
|
chord = tui.Home
|
||||||
case "end":
|
case "end":
|
||||||
chord = tui.End
|
chord = tui.End
|
||||||
|
case "insert":
|
||||||
|
chord = tui.Insert
|
||||||
case "pgup", "page-up":
|
case "pgup", "page-up":
|
||||||
chord = tui.PgUp
|
chord = tui.PgUp
|
||||||
case "pgdn", "page-down":
|
case "pgdn", "page-down":
|
||||||
@@ -652,7 +694,7 @@ func init() {
|
|||||||
// Backreferences are not supported.
|
// Backreferences are not supported.
|
||||||
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
|
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
|
||||||
executeRegexp = regexp.MustCompile(
|
executeRegexp = regexp.MustCompile(
|
||||||
`(?si)[:+](execute(?:-multi|-silent)?|reload):.+|[:+](execute(?:-multi|-silent)?|reload)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
|
`(?si)[:+](execute(?:-multi|-silent)?|reload|preview):.+|[:+](execute(?:-multi|-silent)?|reload|preview)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseKeymap(keymap map[int][]action, str string) {
|
func parseKeymap(keymap map[int][]action, str string) {
|
||||||
@@ -664,6 +706,8 @@ func parseKeymap(keymap map[int][]action, str string) {
|
|||||||
prefix := symbol + "execute"
|
prefix := symbol + "execute"
|
||||||
if strings.HasPrefix(src[1:], "reload") {
|
if strings.HasPrefix(src[1:], "reload") {
|
||||||
prefix = symbol + "reload"
|
prefix = symbol + "reload"
|
||||||
|
} else if strings.HasPrefix(src[1:], "preview") {
|
||||||
|
prefix = symbol + "preview"
|
||||||
} else if src[len(prefix)] == '-' {
|
} else if src[len(prefix)] == '-' {
|
||||||
c := src[len(prefix)+1]
|
c := src[len(prefix)+1]
|
||||||
if c == 's' || c == 'S' {
|
if c == 's' || c == 'S' {
|
||||||
@@ -724,12 +768,16 @@ func parseKeymap(keymap map[int][]action, str string) {
|
|||||||
appendAction(actAcceptNonEmpty)
|
appendAction(actAcceptNonEmpty)
|
||||||
case "print-query":
|
case "print-query":
|
||||||
appendAction(actPrintQuery)
|
appendAction(actPrintQuery)
|
||||||
|
case "refresh-preview":
|
||||||
|
appendAction(actRefreshPreview)
|
||||||
case "replace-query":
|
case "replace-query":
|
||||||
appendAction(actReplaceQuery)
|
appendAction(actReplaceQuery)
|
||||||
case "backward-char":
|
case "backward-char":
|
||||||
appendAction(actBackwardChar)
|
appendAction(actBackwardChar)
|
||||||
case "backward-delete-char":
|
case "backward-delete-char":
|
||||||
appendAction(actBackwardDeleteChar)
|
appendAction(actBackwardDeleteChar)
|
||||||
|
case "backward-delete-char/eof":
|
||||||
|
appendAction(actBackwardDeleteCharEOF)
|
||||||
case "backward-word":
|
case "backward-word":
|
||||||
appendAction(actBackwardWord)
|
appendAction(actBackwardWord)
|
||||||
case "clear-screen":
|
case "clear-screen":
|
||||||
@@ -814,6 +862,10 @@ func parseKeymap(keymap map[int][]action, str string) {
|
|||||||
appendAction(actPreviewPageUp)
|
appendAction(actPreviewPageUp)
|
||||||
case "preview-page-down":
|
case "preview-page-down":
|
||||||
appendAction(actPreviewPageDown)
|
appendAction(actPreviewPageDown)
|
||||||
|
case "preview-half-page-up":
|
||||||
|
appendAction(actPreviewHalfPageUp)
|
||||||
|
case "preview-half-page-down":
|
||||||
|
appendAction(actPreviewHalfPageDown)
|
||||||
default:
|
default:
|
||||||
t := isExecuteAction(specLower)
|
t := isExecuteAction(specLower)
|
||||||
if t == actIgnore {
|
if t == actIgnore {
|
||||||
@@ -827,6 +879,8 @@ func parseKeymap(keymap map[int][]action, str string) {
|
|||||||
switch t {
|
switch t {
|
||||||
case actReload:
|
case actReload:
|
||||||
offset = len("reload")
|
offset = len("reload")
|
||||||
|
case actPreview:
|
||||||
|
offset = len("preview")
|
||||||
case actExecuteSilent:
|
case actExecuteSilent:
|
||||||
offset = len("execute-silent")
|
offset = len("execute-silent")
|
||||||
case actExecuteMulti:
|
case actExecuteMulti:
|
||||||
@@ -864,6 +918,8 @@ func isExecuteAction(str string) actionType {
|
|||||||
switch prefix {
|
switch prefix {
|
||||||
case "reload":
|
case "reload":
|
||||||
return actReload
|
return actReload
|
||||||
|
case "preview":
|
||||||
|
return actPreview
|
||||||
case "execute":
|
case "execute":
|
||||||
return actExecute
|
return actExecute
|
||||||
case "execute-silent":
|
case "execute-silent":
|
||||||
@@ -944,21 +1000,26 @@ func parseInfoStyle(str string) infoStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parsePreviewWindow(opts *previewOpts, input string) {
|
func parsePreviewWindow(opts *previewOpts, input string) {
|
||||||
// Default
|
|
||||||
opts.position = posRight
|
|
||||||
opts.size = sizeSpec{50, true}
|
|
||||||
opts.hidden = false
|
|
||||||
opts.wrap = false
|
|
||||||
|
|
||||||
tokens := strings.Split(input, ":")
|
tokens := strings.Split(input, ":")
|
||||||
sizeRegex := regexp.MustCompile("^[0-9]+%?$")
|
sizeRegex := regexp.MustCompile("^[0-9]+%?$")
|
||||||
|
offsetRegex := regexp.MustCompile("^\\+([0-9]+|{-?[0-9]+})(-[0-9]+|-/[1-9][0-9]*)?$")
|
||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
switch token {
|
switch token {
|
||||||
case "":
|
case "":
|
||||||
|
case "default":
|
||||||
|
*opts = defaultPreviewOpts(opts.command)
|
||||||
case "hidden":
|
case "hidden":
|
||||||
opts.hidden = true
|
opts.hidden = true
|
||||||
|
case "nohidden":
|
||||||
|
opts.hidden = false
|
||||||
case "wrap":
|
case "wrap":
|
||||||
opts.wrap = true
|
opts.wrap = true
|
||||||
|
case "nowrap":
|
||||||
|
opts.wrap = false
|
||||||
|
case "cycle":
|
||||||
|
opts.cycle = true
|
||||||
|
case "nocycle":
|
||||||
|
opts.cycle = false
|
||||||
case "up", "top":
|
case "up", "top":
|
||||||
opts.position = posUp
|
opts.position = posUp
|
||||||
case "down", "bottom":
|
case "down", "bottom":
|
||||||
@@ -967,26 +1028,22 @@ func parsePreviewWindow(opts *previewOpts, input string) {
|
|||||||
opts.position = posLeft
|
opts.position = posLeft
|
||||||
case "right":
|
case "right":
|
||||||
opts.position = posRight
|
opts.position = posRight
|
||||||
case "border":
|
case "rounded", "border":
|
||||||
opts.border = true
|
opts.border = tui.BorderRounded
|
||||||
|
case "sharp":
|
||||||
|
opts.border = tui.BorderSharp
|
||||||
case "noborder":
|
case "noborder":
|
||||||
opts.border = false
|
opts.border = tui.BorderNone
|
||||||
default:
|
default:
|
||||||
if sizeRegex.MatchString(token) {
|
if sizeRegex.MatchString(token) {
|
||||||
opts.size = parseSize(token, 99, "window size")
|
opts.size = parseSize(token, 99, "window size")
|
||||||
|
} else if offsetRegex.MatchString(token) {
|
||||||
|
opts.scroll = token[1:]
|
||||||
} else {
|
} else {
|
||||||
errorExit("invalid preview window layout: " + input)
|
errorExit("invalid preview window option: " + token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !opts.size.percent && opts.size.size > 0 {
|
|
||||||
// Adjust size for border
|
|
||||||
opts.size.size += 2
|
|
||||||
// And padding
|
|
||||||
if opts.position == posLeft || opts.position == posRight {
|
|
||||||
opts.size.size += 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseMargin(margin string) [4]sizeSpec {
|
func parseMargin(margin string) [4]sizeSpec {
|
||||||
@@ -1041,6 +1098,8 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
validateJumpLabels := false
|
validateJumpLabels := false
|
||||||
|
validatePointer := false
|
||||||
|
validateMarker := false
|
||||||
for i := 0; i < len(allArgs); i++ {
|
for i := 0; i < len(allArgs); i++ {
|
||||||
arg := allArgs[i]
|
arg := allArgs[i]
|
||||||
switch arg {
|
switch arg {
|
||||||
@@ -1084,7 +1143,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
case "--bind":
|
case "--bind":
|
||||||
parseKeymap(opts.Keymap, nextString(allArgs, &i, "bind expression required"))
|
parseKeymap(opts.Keymap, nextString(allArgs, &i, "bind expression required"))
|
||||||
case "--color":
|
case "--color":
|
||||||
spec := optionalNextString(allArgs, &i)
|
_, spec := optionalNextString(allArgs, &i)
|
||||||
if len(spec) == 0 {
|
if len(spec) == 0 {
|
||||||
opts.Theme = tui.EmptyTheme()
|
opts.Theme = tui.EmptyTheme()
|
||||||
} else {
|
} else {
|
||||||
@@ -1143,6 +1202,10 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.Cycle = true
|
opts.Cycle = true
|
||||||
case "--no-cycle":
|
case "--no-cycle":
|
||||||
opts.Cycle = false
|
opts.Cycle = false
|
||||||
|
case "--keep-right":
|
||||||
|
opts.KeepRight = true
|
||||||
|
case "--no-keep-right":
|
||||||
|
opts.KeepRight = false
|
||||||
case "--hscroll":
|
case "--hscroll":
|
||||||
opts.Hscroll = true
|
opts.Hscroll = true
|
||||||
case "--no-hscroll":
|
case "--no-hscroll":
|
||||||
@@ -1189,6 +1252,12 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.PrintQuery = false
|
opts.PrintQuery = false
|
||||||
case "--prompt":
|
case "--prompt":
|
||||||
opts.Prompt = nextString(allArgs, &i, "prompt string required")
|
opts.Prompt = nextString(allArgs, &i, "prompt string required")
|
||||||
|
case "--pointer":
|
||||||
|
opts.Pointer = nextString(allArgs, &i, "pointer sign string required")
|
||||||
|
validatePointer = true
|
||||||
|
case "--marker":
|
||||||
|
opts.Marker = nextString(allArgs, &i, "selected sign string required")
|
||||||
|
validateMarker = true
|
||||||
case "--sync":
|
case "--sync":
|
||||||
opts.Sync = true
|
opts.Sync = true
|
||||||
case "--no-sync":
|
case "--no-sync":
|
||||||
@@ -1216,7 +1285,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.Preview.command = ""
|
opts.Preview.command = ""
|
||||||
case "--preview-window":
|
case "--preview-window":
|
||||||
parsePreviewWindow(&opts.Preview,
|
parsePreviewWindow(&opts.Preview,
|
||||||
nextString(allArgs, &i, "preview window layout required: [up|down|left|right][:SIZE[%]][:noborder][:wrap][:hidden]"))
|
nextString(allArgs, &i, "preview window layout required: [up|down|left|right][:SIZE[%]][:rounded|sharp|noborder][:wrap][:cycle][:hidden][:+SCROLL[-OFFSET]][:default]"))
|
||||||
case "--height":
|
case "--height":
|
||||||
opts.Height = parseHeight(nextString(allArgs, &i, "height required: HEIGHT[%]"))
|
opts.Height = parseHeight(nextString(allArgs, &i, "height required: HEIGHT[%]"))
|
||||||
case "--min-height":
|
case "--min-height":
|
||||||
@@ -1226,9 +1295,10 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
case "--no-margin":
|
case "--no-margin":
|
||||||
opts.Margin = defaultMargin()
|
opts.Margin = defaultMargin()
|
||||||
case "--no-border":
|
case "--no-border":
|
||||||
opts.Bordered = false
|
opts.BorderShape = tui.BorderNone
|
||||||
case "--border":
|
case "--border":
|
||||||
opts.Bordered = true
|
hasArg, arg := optionalNextString(allArgs, &i)
|
||||||
|
opts.BorderShape = parseBorder(arg, !hasArg)
|
||||||
case "--no-unicode":
|
case "--no-unicode":
|
||||||
opts.Unicode = false
|
opts.Unicode = false
|
||||||
case "--unicode":
|
case "--unicode":
|
||||||
@@ -1253,8 +1323,16 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.Filter = &value
|
opts.Filter = &value
|
||||||
} else if match, value := optString(arg, "-d", "--delimiter="); match {
|
} else if match, value := optString(arg, "-d", "--delimiter="); match {
|
||||||
opts.Delimiter = delimiterRegexp(value)
|
opts.Delimiter = delimiterRegexp(value)
|
||||||
|
} else if match, value := optString(arg, "--border="); match {
|
||||||
|
opts.BorderShape = parseBorder(value, false)
|
||||||
} else if match, value := optString(arg, "--prompt="); match {
|
} else if match, value := optString(arg, "--prompt="); match {
|
||||||
opts.Prompt = value
|
opts.Prompt = value
|
||||||
|
} else if match, value := optString(arg, "--pointer="); match {
|
||||||
|
opts.Pointer = value
|
||||||
|
validatePointer = true
|
||||||
|
} else if match, value := optString(arg, "--marker="); match {
|
||||||
|
opts.Marker = value
|
||||||
|
validateMarker = true
|
||||||
} else if match, value := optString(arg, "-n", "--nth="); match {
|
} else if match, value := optString(arg, "-n", "--nth="); match {
|
||||||
opts.Nth = splitNth(value)
|
opts.Nth = splitNth(value)
|
||||||
} else if match, value := optString(arg, "--with-nth="); match {
|
} else if match, value := optString(arg, "--with-nth="); match {
|
||||||
@@ -1303,6 +1381,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.HscrollOff = atoi(value)
|
opts.HscrollOff = atoi(value)
|
||||||
} else if match, value := optString(arg, "--jump-labels="); match {
|
} else if match, value := optString(arg, "--jump-labels="); match {
|
||||||
opts.JumpLabels = value
|
opts.JumpLabels = value
|
||||||
|
validateJumpLabels = true
|
||||||
} else {
|
} else {
|
||||||
errorExit("unknown option: " + arg)
|
errorExit("unknown option: " + arg)
|
||||||
}
|
}
|
||||||
@@ -1332,11 +1411,40 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if validatePointer {
|
||||||
|
if err := validateSign(opts.Pointer, "pointer"); err != nil {
|
||||||
|
errorExit(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if validateMarker {
|
||||||
|
if err := validateSign(opts.Marker, "marker"); err != nil {
|
||||||
|
errorExit(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSign(sign string, signOptName string) error {
|
||||||
|
if sign == "" {
|
||||||
|
return fmt.Errorf("%v cannot be empty", signOptName)
|
||||||
|
}
|
||||||
|
widthSum := 0
|
||||||
|
for _, r := range sign {
|
||||||
|
if !unicode.IsGraphic(r) {
|
||||||
|
return fmt.Errorf("invalid character in %v", signOptName)
|
||||||
|
}
|
||||||
|
widthSum += runewidth.RuneWidth(r)
|
||||||
|
if widthSum > 2 {
|
||||||
|
return fmt.Errorf("%v display width should be up to 2", signOptName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func postProcessOptions(opts *Options) {
|
func postProcessOptions(opts *Options) {
|
||||||
if util.IsWindows() && opts.Height.size > 0 {
|
if !tui.IsLightRendererSupported() && opts.Height.size > 0 {
|
||||||
errorExit("--height option is currently not supported on Windows")
|
errorExit("--height option is currently not supported on this platform")
|
||||||
}
|
}
|
||||||
// Default actions for CTRL-N / CTRL-P when --history is set
|
// Default actions for CTRL-N / CTRL-P when --history is set
|
||||||
if opts.History != nil {
|
if opts.History != nil {
|
||||||
|
@@ -387,23 +387,26 @@ func TestPreviewOpts(t *testing.T) {
|
|||||||
opts.Preview.size.size == 50) {
|
opts.Preview.size.size == 50) {
|
||||||
t.Error()
|
t.Error()
|
||||||
}
|
}
|
||||||
opts = optsFor("--preview", "cat {}", "--preview-window=left:15:hidden:wrap")
|
opts = optsFor("--preview", "cat {}", "--preview-window=left:15:hidden:wrap:+{1}-/2")
|
||||||
if !(opts.Preview.command == "cat {}" &&
|
if !(opts.Preview.command == "cat {}" &&
|
||||||
opts.Preview.hidden == true &&
|
opts.Preview.hidden == true &&
|
||||||
opts.Preview.wrap == true &&
|
opts.Preview.wrap == true &&
|
||||||
opts.Preview.position == posLeft &&
|
opts.Preview.position == posLeft &&
|
||||||
|
opts.Preview.scroll == "{1}-/2" &&
|
||||||
opts.Preview.size.percent == false &&
|
opts.Preview.size.percent == false &&
|
||||||
opts.Preview.size.size == 15+2+2) {
|
opts.Preview.size.size == 15) {
|
||||||
t.Error(opts.Preview)
|
t.Error(opts.Preview)
|
||||||
}
|
}
|
||||||
opts = optsFor("--preview-window=up:15:wrap:hidden", "--preview-window=down")
|
opts = optsFor("--preview-window=up:15:wrap:hidden:+{1}-/2", "--preview-window=down", "--preview-window=cycle")
|
||||||
if !(opts.Preview.command == "" &&
|
if !(opts.Preview.command == "" &&
|
||||||
opts.Preview.hidden == false &&
|
opts.Preview.hidden == true &&
|
||||||
opts.Preview.wrap == false &&
|
opts.Preview.wrap == true &&
|
||||||
|
opts.Preview.cycle == true &&
|
||||||
opts.Preview.position == posDown &&
|
opts.Preview.position == posDown &&
|
||||||
opts.Preview.size.percent == true &&
|
opts.Preview.scroll == "{1}-/2" &&
|
||||||
opts.Preview.size.size == 50) {
|
opts.Preview.size.percent == false &&
|
||||||
t.Error(opts.Preview)
|
opts.Preview.size.size == 15) {
|
||||||
|
t.Error(opts.Preview.size.size)
|
||||||
}
|
}
|
||||||
opts = optsFor("--preview-window=up:15:wrap:hidden")
|
opts = optsFor("--preview-window=up:15:wrap:hidden")
|
||||||
if !(opts.Preview.command == "" &&
|
if !(opts.Preview.command == "" &&
|
||||||
@@ -411,7 +414,14 @@ func TestPreviewOpts(t *testing.T) {
|
|||||||
opts.Preview.wrap == true &&
|
opts.Preview.wrap == true &&
|
||||||
opts.Preview.position == posUp &&
|
opts.Preview.position == posUp &&
|
||||||
opts.Preview.size.percent == false &&
|
opts.Preview.size.percent == false &&
|
||||||
opts.Preview.size.size == 15+2) {
|
opts.Preview.size.size == 15) {
|
||||||
|
t.Error(opts.Preview)
|
||||||
|
}
|
||||||
|
opts = optsFor("--preview=foo", "--preview-window=up", "--preview-window=default:70%")
|
||||||
|
if !(opts.Preview.command == "foo" &&
|
||||||
|
opts.Preview.position == posRight &&
|
||||||
|
opts.Preview.size.percent == true &&
|
||||||
|
opts.Preview.size.size == 70) {
|
||||||
t.Error(opts.Preview)
|
t.Error(opts.Preview)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -422,3 +432,29 @@ func TestAdditiveExpect(t *testing.T) {
|
|||||||
t.Error(opts.Expect)
|
t.Error(opts.Expect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateSign(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
inputSign string
|
||||||
|
isValid bool
|
||||||
|
}{
|
||||||
|
{"> ", true},
|
||||||
|
{"아", true},
|
||||||
|
{"😀", true},
|
||||||
|
{"", false},
|
||||||
|
{">>>", false},
|
||||||
|
{"\n", false},
|
||||||
|
{"\t", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
err := validateSign(testCase.inputSign, "")
|
||||||
|
if testCase.isValid && err != nil {
|
||||||
|
t.Errorf("Input sign `%s` caused error", testCase.inputSign)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testCase.isValid && err == nil {
|
||||||
|
t.Errorf("Input sign `%s` did not cause error", testCase.inputSign)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -33,6 +33,7 @@ type term struct {
|
|||||||
inv bool
|
inv bool
|
||||||
text []rune
|
text []rune
|
||||||
caseSensitive bool
|
caseSensitive bool
|
||||||
|
normalize bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the string representation of a term.
|
// String returns the string representation of a term.
|
||||||
@@ -128,6 +129,8 @@ func BuildPattern(fuzzy bool, fuzzyAlgo algo.Algo, extended bool, caseMode Case,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lowerString := strings.ToLower(asString)
|
lowerString := strings.ToLower(asString)
|
||||||
|
normalize = normalize &&
|
||||||
|
lowerString == string(algo.NormalizeRunes([]rune(lowerString)))
|
||||||
caseSensitive = caseMode == CaseRespect ||
|
caseSensitive = caseMode == CaseRespect ||
|
||||||
caseMode == CaseSmart && lowerString != asString
|
caseMode == CaseSmart && lowerString != asString
|
||||||
if !caseSensitive {
|
if !caseSensitive {
|
||||||
@@ -173,6 +176,8 @@ func parseTerms(fuzzy bool, caseMode Case, normalize bool, str string) []termSet
|
|||||||
lowerText := strings.ToLower(text)
|
lowerText := strings.ToLower(text)
|
||||||
caseSensitive := caseMode == CaseRespect ||
|
caseSensitive := caseMode == CaseRespect ||
|
||||||
caseMode == CaseSmart && text != lowerText
|
caseMode == CaseSmart && text != lowerText
|
||||||
|
normalizeTerm := normalize &&
|
||||||
|
lowerText == string(algo.NormalizeRunes([]rune(lowerText)))
|
||||||
if !caseSensitive {
|
if !caseSensitive {
|
||||||
text = lowerText
|
text = lowerText
|
||||||
}
|
}
|
||||||
@@ -222,14 +227,15 @@ func parseTerms(fuzzy bool, caseMode Case, normalize bool, str string) []termSet
|
|||||||
set = termSet{}
|
set = termSet{}
|
||||||
}
|
}
|
||||||
textRunes := []rune(text)
|
textRunes := []rune(text)
|
||||||
if normalize {
|
if normalizeTerm {
|
||||||
textRunes = algo.NormalizeRunes(textRunes)
|
textRunes = algo.NormalizeRunes(textRunes)
|
||||||
}
|
}
|
||||||
set = append(set, term{
|
set = append(set, term{
|
||||||
typ: typ,
|
typ: typ,
|
||||||
inv: inv,
|
inv: inv,
|
||||||
text: textRunes,
|
text: textRunes,
|
||||||
caseSensitive: caseSensitive})
|
caseSensitive: caseSensitive,
|
||||||
|
normalize: normalizeTerm})
|
||||||
switchSet = true
|
switchSet = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,7 +366,7 @@ func (p *Pattern) extendedMatch(item *Item, withPos bool, slab *util.Slab) ([]Of
|
|||||||
matched := false
|
matched := false
|
||||||
for _, term := range termSet {
|
for _, term := range termSet {
|
||||||
pfun := p.procFun[term.typ]
|
pfun := p.procFun[term.typ]
|
||||||
off, score, pos := p.iter(pfun, input, term.caseSensitive, p.normalize, p.forward, term.text, withPos, slab)
|
off, score, pos := p.iter(pfun, input, term.caseSensitive, term.normalize, p.forward, term.text, withPos, slab)
|
||||||
if sidx := off[0]; sidx >= 0 {
|
if sidx := off[0]; sidx >= 0 {
|
||||||
if term.inv {
|
if term.inv {
|
||||||
continue
|
continue
|
||||||
|
@@ -98,6 +98,9 @@ func TestEqual(t *testing.T) {
|
|||||||
}
|
}
|
||||||
match("ABC", -1, -1)
|
match("ABC", -1, -1)
|
||||||
match("AbC", 0, 3)
|
match("AbC", 0, 3)
|
||||||
|
match("AbC ", 0, 3)
|
||||||
|
match(" AbC ", 1, 4)
|
||||||
|
match(" AbC", 2, 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCaseSensitivity(t *testing.T) {
|
func TestCaseSensitivity(t *testing.T) {
|
||||||
|
8
src/protector/protector.go
Normal file
8
src/protector/protector.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// +build !openbsd
|
||||||
|
|
||||||
|
package protector
|
||||||
|
|
||||||
|
// Protect calls OS specific protections like pledge on OpenBSD
|
||||||
|
func Protect() {
|
||||||
|
return
|
||||||
|
}
|
10
src/protector/protector_openbsd.go
Normal file
10
src/protector/protector_openbsd.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// +build openbsd
|
||||||
|
|
||||||
|
package protector
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
// Protect calls OS specific protections like pledge on OpenBSD
|
||||||
|
func Protect() {
|
||||||
|
unix.PledgePromises("stdio rpath tty proc exec")
|
||||||
|
}
|
@@ -2,14 +2,17 @@ package fzf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
|
"github.com/saracen/walker"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reader reads from command or standard input
|
// Reader reads from command or standard input
|
||||||
@@ -78,7 +81,7 @@ func (r *Reader) terminate() {
|
|||||||
r.killed = true
|
r.killed = true
|
||||||
if r.exec != nil && r.exec.Process != nil {
|
if r.exec != nil && r.exec.Process != nil {
|
||||||
util.KillCommand(r.exec)
|
util.KillCommand(r.exec)
|
||||||
} else {
|
} else if defaultCommand != "" {
|
||||||
os.Stdin.Close()
|
os.Stdin.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +102,11 @@ func (r *Reader) ReadSource() {
|
|||||||
shell := "bash"
|
shell := "bash"
|
||||||
cmd := os.Getenv("FZF_DEFAULT_COMMAND")
|
cmd := os.Getenv("FZF_DEFAULT_COMMAND")
|
||||||
if len(cmd) == 0 {
|
if len(cmd) == 0 {
|
||||||
|
if defaultCommand != "" {
|
||||||
success = r.readFromCommand(&shell, defaultCommand)
|
success = r.readFromCommand(&shell, defaultCommand)
|
||||||
|
} else {
|
||||||
|
success = r.readFiles()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
success = r.readFromCommand(nil, cmd)
|
success = r.readFromCommand(nil, cmd)
|
||||||
}
|
}
|
||||||
@@ -144,6 +151,32 @@ func (r *Reader) readFromStdin() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Reader) readFiles() bool {
|
||||||
|
r.killed = false
|
||||||
|
fn := func(path string, mode os.FileInfo) error {
|
||||||
|
path = filepath.Clean(path)
|
||||||
|
if path != "." {
|
||||||
|
isDir := mode.Mode().IsDir()
|
||||||
|
if isDir && filepath.Base(path)[0] == '.' {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
if !isDir && r.pusher([]byte(path)) {
|
||||||
|
atomic.StoreInt32(&r.event, int32(EvtReadNew))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.mutex.Lock()
|
||||||
|
defer r.mutex.Unlock()
|
||||||
|
if r.killed {
|
||||||
|
return context.Canceled
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cb := walker.WithErrorCallback(func(pathname string, err error) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return walker.Walk(".", fn, cb) == nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Reader) readFromCommand(shell *string, command string) bool {
|
func (r *Reader) readFromCommand(shell *string, command string) bool {
|
||||||
r.mutex.Lock()
|
r.mutex.Lock()
|
||||||
r.killed = false
|
r.killed = false
|
||||||
|
376
src/terminal.go
376
src/terminal.go
@@ -23,10 +23,14 @@ import (
|
|||||||
// import "github.com/pkg/profile"
|
// import "github.com/pkg/profile"
|
||||||
|
|
||||||
var placeholder *regexp.Regexp
|
var placeholder *regexp.Regexp
|
||||||
|
var numericPrefix *regexp.Regexp
|
||||||
var activeTempFiles []string
|
var activeTempFiles []string
|
||||||
|
|
||||||
|
const ellipsis string = ".."
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{\+?f?nf?})`)
|
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{\+?f?nf?})`)
|
||||||
|
numericPrefix = regexp.MustCompile(`^[[:punct:]]*([0-9]+)`)
|
||||||
activeTempFiles = []string{}
|
activeTempFiles = []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,11 +65,19 @@ var emptyLine = itemLine{}
|
|||||||
type Terminal struct {
|
type Terminal struct {
|
||||||
initDelay time.Duration
|
initDelay time.Duration
|
||||||
infoStyle infoStyle
|
infoStyle infoStyle
|
||||||
prompt string
|
spinner []string
|
||||||
|
prompt func()
|
||||||
promptLen int
|
promptLen int
|
||||||
|
pointer string
|
||||||
|
pointerLen int
|
||||||
|
pointerEmpty string
|
||||||
|
marker string
|
||||||
|
markerLen int
|
||||||
|
markerEmpty string
|
||||||
queryLen [2]int
|
queryLen [2]int
|
||||||
layout layoutType
|
layout layoutType
|
||||||
fullscreen bool
|
fullscreen bool
|
||||||
|
keepRight bool
|
||||||
hscroll bool
|
hscroll bool
|
||||||
hscrollOff int
|
hscrollOff int
|
||||||
wordRubout string
|
wordRubout string
|
||||||
@@ -93,7 +105,7 @@ type Terminal struct {
|
|||||||
margin [4]sizeSpec
|
margin [4]sizeSpec
|
||||||
strong tui.Attr
|
strong tui.Attr
|
||||||
unicode bool
|
unicode bool
|
||||||
bordered bool
|
borderShape tui.BorderShape
|
||||||
cleanExit bool
|
cleanExit bool
|
||||||
border tui.Window
|
border tui.Window
|
||||||
window tui.Window
|
window tui.Window
|
||||||
@@ -145,8 +157,6 @@ func (a byTimeOrder) Less(i, j int) bool {
|
|||||||
return a[i].at.Before(a[j].at)
|
return a[i].at.Before(a[j].at)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _spinner = []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
reqPrompt util.EventType = iota
|
reqPrompt util.EventType = iota
|
||||||
reqInfo
|
reqInfo
|
||||||
@@ -182,6 +192,7 @@ const (
|
|||||||
actAcceptNonEmpty
|
actAcceptNonEmpty
|
||||||
actBackwardChar
|
actBackwardChar
|
||||||
actBackwardDeleteChar
|
actBackwardDeleteChar
|
||||||
|
actBackwardDeleteCharEOF
|
||||||
actBackwardWord
|
actBackwardWord
|
||||||
actCancel
|
actCancel
|
||||||
actClearScreen
|
actClearScreen
|
||||||
@@ -215,14 +226,18 @@ const (
|
|||||||
actJump
|
actJump
|
||||||
actJumpAccept
|
actJumpAccept
|
||||||
actPrintQuery
|
actPrintQuery
|
||||||
|
actRefreshPreview
|
||||||
actReplaceQuery
|
actReplaceQuery
|
||||||
actToggleSort
|
actToggleSort
|
||||||
actTogglePreview
|
actTogglePreview
|
||||||
actTogglePreviewWrap
|
actTogglePreviewWrap
|
||||||
|
actPreview
|
||||||
actPreviewUp
|
actPreviewUp
|
||||||
actPreviewDown
|
actPreviewDown
|
||||||
actPreviewPageUp
|
actPreviewPageUp
|
||||||
actPreviewPageDown
|
actPreviewPageDown
|
||||||
|
actPreviewHalfPageUp
|
||||||
|
actPreviewHalfPageDown
|
||||||
actPreviousHistory
|
actPreviousHistory
|
||||||
actNextHistory
|
actNextHistory
|
||||||
actExecute
|
actExecute
|
||||||
@@ -246,6 +261,16 @@ type searchRequest struct {
|
|||||||
command *string
|
command *string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type previewRequest struct {
|
||||||
|
template string
|
||||||
|
list []*Item
|
||||||
|
}
|
||||||
|
|
||||||
|
type previewResult struct {
|
||||||
|
content string
|
||||||
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
func toActions(types ...actionType) []action {
|
func toActions(types ...actionType) []action {
|
||||||
actions := make([]action, len(types))
|
actions := make([]action, len(types))
|
||||||
for idx, t := range types {
|
for idx, t := range types {
|
||||||
@@ -317,6 +342,17 @@ func trimQuery(query string) []rune {
|
|||||||
return []rune(strings.Replace(query, "\t", " ", -1))
|
return []rune(strings.Replace(query, "\t", " ", -1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasPreviewAction(opts *Options) bool {
|
||||||
|
for _, actions := range opts.Keymap {
|
||||||
|
for _, action := range actions {
|
||||||
|
if action.t == actPreview {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// NewTerminal returns new Terminal object
|
// NewTerminal returns new Terminal object
|
||||||
func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||||
input := trimQuery(opts.Query)
|
input := trimQuery(opts.Query)
|
||||||
@@ -334,7 +370,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
delay = initialDelay
|
delay = initialDelay
|
||||||
}
|
}
|
||||||
var previewBox *util.EventBox
|
var previewBox *util.EventBox
|
||||||
if len(opts.Preview.command) > 0 {
|
if len(opts.Preview.command) > 0 || hasPreviewAction(opts) {
|
||||||
previewBox = util.NewEventBox()
|
previewBox = util.NewEventBox()
|
||||||
}
|
}
|
||||||
strongAttr := tui.Bold
|
strongAttr := tui.Bold
|
||||||
@@ -364,28 +400,34 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
effectiveMinHeight *= 2
|
effectiveMinHeight *= 2
|
||||||
}
|
}
|
||||||
if opts.InfoStyle != infoDefault {
|
if opts.InfoStyle != infoDefault {
|
||||||
effectiveMinHeight -= 1
|
effectiveMinHeight--
|
||||||
}
|
}
|
||||||
if opts.Bordered {
|
if opts.BorderShape != tui.BorderNone {
|
||||||
effectiveMinHeight += 2
|
effectiveMinHeight += 2
|
||||||
}
|
}
|
||||||
return util.Min(termHeight, util.Max(maxHeight, effectiveMinHeight))
|
return util.Min(termHeight, util.Max(maxHeight, effectiveMinHeight))
|
||||||
}
|
}
|
||||||
renderer = tui.NewLightRenderer(opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, opts.ClearOnExit, false, maxHeightFunc)
|
renderer = tui.NewLightRenderer(opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, opts.ClearOnExit, false, maxHeightFunc)
|
||||||
}
|
}
|
||||||
wordRubout := "[^[:alnum:]][[:alnum:]]"
|
wordRubout := "[^\\pL\\pN][\\pL\\pN]"
|
||||||
wordNext := "[[:alnum:]][^[:alnum:]]|(.$)"
|
wordNext := "[\\pL\\pN][^\\pL\\pN]|(.$)"
|
||||||
if opts.FileWord {
|
if opts.FileWord {
|
||||||
sep := regexp.QuoteMeta(string(os.PathSeparator))
|
sep := regexp.QuoteMeta(string(os.PathSeparator))
|
||||||
wordRubout = fmt.Sprintf("%s[^%s]", sep, sep)
|
wordRubout = fmt.Sprintf("%s[^%s]", sep, sep)
|
||||||
wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep)
|
wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep)
|
||||||
}
|
}
|
||||||
|
spinner := []string{`⠋`, `⠙`, `⠹`, `⠸`, `⠼`, `⠴`, `⠦`, `⠧`, `⠇`, `⠏`}
|
||||||
|
if !opts.Unicode {
|
||||||
|
spinner = []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
|
||||||
|
}
|
||||||
t := Terminal{
|
t := Terminal{
|
||||||
initDelay: delay,
|
initDelay: delay,
|
||||||
infoStyle: opts.InfoStyle,
|
infoStyle: opts.InfoStyle,
|
||||||
|
spinner: spinner,
|
||||||
queryLen: [2]int{0, 0},
|
queryLen: [2]int{0, 0},
|
||||||
layout: opts.Layout,
|
layout: opts.Layout,
|
||||||
fullscreen: fullscreen,
|
fullscreen: fullscreen,
|
||||||
|
keepRight: opts.KeepRight,
|
||||||
hscroll: opts.Hscroll,
|
hscroll: opts.Hscroll,
|
||||||
hscrollOff: opts.HscrollOff,
|
hscrollOff: opts.HscrollOff,
|
||||||
wordRubout: wordRubout,
|
wordRubout: wordRubout,
|
||||||
@@ -407,7 +449,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
history: opts.History,
|
history: opts.History,
|
||||||
margin: opts.Margin,
|
margin: opts.Margin,
|
||||||
unicode: opts.Unicode,
|
unicode: opts.Unicode,
|
||||||
bordered: opts.Bordered,
|
borderShape: opts.BorderShape,
|
||||||
cleanExit: opts.ClearOnExit,
|
cleanExit: opts.ClearOnExit,
|
||||||
strong: strongAttr,
|
strong: strongAttr,
|
||||||
cycle: opts.Cycle,
|
cycle: opts.Cycle,
|
||||||
@@ -436,10 +478,29 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
killChan: make(chan int),
|
killChan: make(chan int),
|
||||||
tui: renderer,
|
tui: renderer,
|
||||||
initFunc: func() { renderer.Init() }}
|
initFunc: func() { renderer.Init() }}
|
||||||
t.prompt, t.promptLen = t.processTabs([]rune(opts.Prompt), 0)
|
t.prompt, t.promptLen = t.parsePrompt(opts.Prompt)
|
||||||
|
t.pointer, t.pointerLen = t.processTabs([]rune(opts.Pointer), 0)
|
||||||
|
t.marker, t.markerLen = t.processTabs([]rune(opts.Marker), 0)
|
||||||
|
// Pre-calculated empty pointer and marker signs
|
||||||
|
t.pointerEmpty = strings.Repeat(" ", t.pointerLen)
|
||||||
|
t.markerEmpty = strings.Repeat(" ", t.markerLen)
|
||||||
|
|
||||||
return &t
|
return &t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) parsePrompt(prompt string) (func(), int) {
|
||||||
|
var state *ansiState
|
||||||
|
trimmed, colors, _ := extractColor(prompt, state, nil)
|
||||||
|
item := &Item{text: util.ToChars([]byte(trimmed)), colors: colors}
|
||||||
|
output := func() {
|
||||||
|
t.printHighlighted(
|
||||||
|
Result{item: item}, t.strong, tui.ColPrompt, tui.ColPrompt, false, false)
|
||||||
|
}
|
||||||
|
_, promptLen := t.processTabs([]rune(trimmed), 0)
|
||||||
|
|
||||||
|
return output, promptLen
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Terminal) noInfoLine() bool {
|
func (t *Terminal) noInfoLine() bool {
|
||||||
return t.infoStyle != infoDefault
|
return t.infoStyle != infoDefault
|
||||||
}
|
}
|
||||||
@@ -553,12 +614,12 @@ const (
|
|||||||
maxDisplayWidthCalc = 1024
|
maxDisplayWidthCalc = 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
func calculateSize(base int, size sizeSpec, margin int, minSize int) int {
|
func calculateSize(base int, size sizeSpec, occupied int, minSize int, pad int) int {
|
||||||
max := base - margin
|
max := base - occupied
|
||||||
if size.percent {
|
if size.percent {
|
||||||
return util.Constrain(int(float64(base)*0.01*size.size), minSize, max)
|
return util.Constrain(int(float64(base)*0.01*size.size), minSize, max)
|
||||||
}
|
}
|
||||||
return util.Constrain(int(size.size), minSize, max)
|
return util.Constrain(int(size.size)+pad, minSize, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) resizeWindows() {
|
func (t *Terminal) resizeWindows() {
|
||||||
@@ -578,8 +639,11 @@ func (t *Terminal) resizeWindows() {
|
|||||||
} else {
|
} else {
|
||||||
marginInt[idx] = int(sizeSpec.size)
|
marginInt[idx] = int(sizeSpec.size)
|
||||||
}
|
}
|
||||||
if t.bordered && idx%2 == 0 {
|
switch t.borderShape {
|
||||||
marginInt[idx] += 1
|
case tui.BorderHorizontal:
|
||||||
|
marginInt[idx] += 1 - idx%2
|
||||||
|
case tui.BorderRounded, tui.BorderSharp:
|
||||||
|
marginInt[idx] += 1 + idx%2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
adjust := func(idx1 int, idx2 int, max int, min int) {
|
adjust := func(idx1 int, idx2 int, max int, min int) {
|
||||||
@@ -614,54 +678,77 @@ func (t *Terminal) resizeWindows() {
|
|||||||
}
|
}
|
||||||
if t.pborder != nil {
|
if t.pborder != nil {
|
||||||
t.pborder.Close()
|
t.pborder.Close()
|
||||||
|
}
|
||||||
|
if t.pwindow != nil {
|
||||||
t.pwindow.Close()
|
t.pwindow.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
width := screenWidth - marginInt[1] - marginInt[3]
|
width := screenWidth - marginInt[1] - marginInt[3]
|
||||||
height := screenHeight - marginInt[0] - marginInt[2]
|
height := screenHeight - marginInt[0] - marginInt[2]
|
||||||
if t.bordered {
|
switch t.borderShape {
|
||||||
|
case tui.BorderHorizontal:
|
||||||
t.border = t.tui.NewWindow(
|
t.border = t.tui.NewWindow(
|
||||||
marginInt[0]-1,
|
marginInt[0]-1,
|
||||||
marginInt[3],
|
marginInt[3],
|
||||||
width,
|
width,
|
||||||
height+2,
|
height+2,
|
||||||
false, tui.MakeBorderStyle(tui.BorderHorizontal, t.unicode))
|
false, tui.MakeBorderStyle(tui.BorderHorizontal, t.unicode))
|
||||||
|
case tui.BorderRounded, tui.BorderSharp:
|
||||||
|
t.border = t.tui.NewWindow(
|
||||||
|
marginInt[0]-1,
|
||||||
|
marginInt[3]-2,
|
||||||
|
width+4,
|
||||||
|
height+2,
|
||||||
|
false, tui.MakeBorderStyle(t.borderShape, t.unicode))
|
||||||
}
|
}
|
||||||
noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode)
|
noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode)
|
||||||
if previewVisible {
|
if previewVisible {
|
||||||
createPreviewWindow := func(y int, x int, w int, h int) {
|
createPreviewWindow := func(y int, x int, w int, h int) {
|
||||||
previewBorder := tui.MakeBorderStyle(tui.BorderAround, t.unicode)
|
pwidth := w
|
||||||
if !t.preview.border {
|
pheight := h
|
||||||
previewBorder = tui.MakeTransparentBorder()
|
if t.preview.border != tui.BorderNone {
|
||||||
}
|
previewBorder := tui.MakeBorderStyle(t.preview.border, t.unicode)
|
||||||
t.pborder = t.tui.NewWindow(y, x, w, h, true, previewBorder)
|
t.pborder = t.tui.NewWindow(y, x, w, h, true, previewBorder)
|
||||||
pwidth := w - 4
|
pwidth -= 4
|
||||||
|
pheight -= 2
|
||||||
|
x += 2
|
||||||
|
y += 1
|
||||||
|
} else {
|
||||||
|
previewBorder := tui.MakeTransparentBorder()
|
||||||
|
t.pborder = t.tui.NewWindow(y, x, w, h, true, previewBorder)
|
||||||
|
pwidth -= 4
|
||||||
|
x += 2
|
||||||
|
}
|
||||||
// ncurses auto-wraps the line when the cursor reaches the right-end of
|
// ncurses auto-wraps the line when the cursor reaches the right-end of
|
||||||
// the window. To prevent unintended line-wraps, we use the width one
|
// the window. To prevent unintended line-wraps, we use the width one
|
||||||
// column larger than the desired value.
|
// column larger than the desired value.
|
||||||
if !t.preview.wrap && t.tui.DoesAutoWrap() {
|
if !t.preview.wrap && t.tui.DoesAutoWrap() {
|
||||||
pwidth += 1
|
pwidth += 1
|
||||||
}
|
}
|
||||||
t.pwindow = t.tui.NewWindow(y+1, x+2, pwidth, h-2, true, noBorder)
|
t.pwindow = t.tui.NewWindow(y, x, pwidth, pheight, true, noBorder)
|
||||||
|
}
|
||||||
|
verticalPad := 2
|
||||||
|
if t.preview.border == tui.BorderNone {
|
||||||
|
verticalPad = 0
|
||||||
}
|
}
|
||||||
switch t.preview.position {
|
switch t.preview.position {
|
||||||
case posUp:
|
case posUp:
|
||||||
pheight := calculateSize(height, t.preview.size, minHeight, 3)
|
pheight := calculateSize(height, t.preview.size, minHeight, 3, verticalPad)
|
||||||
t.window = t.tui.NewWindow(
|
t.window = t.tui.NewWindow(
|
||||||
marginInt[0]+pheight, marginInt[3], width, height-pheight, false, noBorder)
|
marginInt[0]+pheight, marginInt[3], width, height-pheight, false, noBorder)
|
||||||
createPreviewWindow(marginInt[0], marginInt[3], width, pheight)
|
createPreviewWindow(marginInt[0], marginInt[3], width, pheight)
|
||||||
case posDown:
|
case posDown:
|
||||||
pheight := calculateSize(height, t.preview.size, minHeight, 3)
|
pheight := calculateSize(height, t.preview.size, minHeight, 3, verticalPad)
|
||||||
t.window = t.tui.NewWindow(
|
t.window = t.tui.NewWindow(
|
||||||
marginInt[0], marginInt[3], width, height-pheight, false, noBorder)
|
marginInt[0], marginInt[3], width, height-pheight, false, noBorder)
|
||||||
createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
|
createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
|
||||||
case posLeft:
|
case posLeft:
|
||||||
pwidth := calculateSize(width, t.preview.size, minWidth, 5)
|
pwidth := calculateSize(width, t.preview.size, minWidth, 5, 4)
|
||||||
t.window = t.tui.NewWindow(
|
t.window = t.tui.NewWindow(
|
||||||
marginInt[0], marginInt[3]+pwidth, width-pwidth, height, false, noBorder)
|
marginInt[0], marginInt[3]+pwidth, width-pwidth, height, false, noBorder)
|
||||||
createPreviewWindow(marginInt[0], marginInt[3], pwidth, height)
|
createPreviewWindow(marginInt[0], marginInt[3], pwidth, height)
|
||||||
case posRight:
|
case posRight:
|
||||||
pwidth := calculateSize(width, t.preview.size, minWidth, 5)
|
pwidth := calculateSize(width, t.preview.size, minWidth, 5, 4)
|
||||||
t.window = t.tui.NewWindow(
|
t.window = t.tui.NewWindow(
|
||||||
marginInt[0], marginInt[3], width-pwidth, height, false, noBorder)
|
marginInt[0], marginInt[3], width-pwidth, height, false, noBorder)
|
||||||
createPreviewWindow(marginInt[0], marginInt[3]+width-pwidth, pwidth, height)
|
createPreviewWindow(marginInt[0], marginInt[3]+width-pwidth, pwidth, height)
|
||||||
@@ -730,7 +817,7 @@ func (t *Terminal) placeCursor() {
|
|||||||
|
|
||||||
func (t *Terminal) printPrompt() {
|
func (t *Terminal) printPrompt() {
|
||||||
t.move(0, 0, true)
|
t.move(0, 0, true)
|
||||||
t.window.CPrint(tui.ColPrompt, t.strong, t.prompt)
|
t.prompt()
|
||||||
|
|
||||||
before, after := t.updatePromptOffset()
|
before, after := t.updatePromptOffset()
|
||||||
t.window.CPrint(tui.ColNormal, t.strong, string(before))
|
t.window.CPrint(tui.ColNormal, t.strong, string(before))
|
||||||
@@ -744,8 +831,8 @@ func (t *Terminal) printInfo() {
|
|||||||
t.move(1, 0, true)
|
t.move(1, 0, true)
|
||||||
if t.reading {
|
if t.reading {
|
||||||
duration := int64(spinnerDuration)
|
duration := int64(spinnerDuration)
|
||||||
idx := (time.Now().UnixNano() % (duration * int64(len(_spinner)))) / duration
|
idx := (time.Now().UnixNano() % (duration * int64(len(t.spinner)))) / duration
|
||||||
t.window.CPrint(tui.ColSpinner, t.strong, _spinner[idx])
|
t.window.CPrint(tui.ColSpinner, t.strong, t.spinner[idx])
|
||||||
}
|
}
|
||||||
t.move(1, 2, false)
|
t.move(1, 2, false)
|
||||||
pos = 2
|
pos = 2
|
||||||
@@ -791,7 +878,7 @@ func (t *Terminal) printInfo() {
|
|||||||
maxWidth := t.window.Width() - pos
|
maxWidth := t.window.Width() - pos
|
||||||
if len(output) > maxWidth {
|
if len(output) > maxWidth {
|
||||||
outputRunes, _ := t.trimRight([]rune(output), maxWidth-2)
|
outputRunes, _ := t.trimRight([]rune(output), maxWidth-2)
|
||||||
output = string(outputRunes) + ".."
|
output = string(outputRunes) + strings.Repeat(".", util.Constrain(maxWidth, 0, 2))
|
||||||
}
|
}
|
||||||
t.window.CPrint(tui.ColInfo, 0, output)
|
t.window.CPrint(tui.ColInfo, 0, output)
|
||||||
}
|
}
|
||||||
@@ -848,15 +935,15 @@ func (t *Terminal) printList() {
|
|||||||
func (t *Terminal) printItem(result Result, line int, i int, current bool) {
|
func (t *Terminal) printItem(result Result, line int, i int, current bool) {
|
||||||
item := result.item
|
item := result.item
|
||||||
_, selected := t.selected[item.Index()]
|
_, selected := t.selected[item.Index()]
|
||||||
label := " "
|
label := t.pointerEmpty
|
||||||
if t.jumping != jumpDisabled {
|
if t.jumping != jumpDisabled {
|
||||||
if i < len(t.jumpLabels) {
|
if i < len(t.jumpLabels) {
|
||||||
// Striped
|
// Striped
|
||||||
current = i%2 == 0
|
current = i%2 == 0
|
||||||
label = t.jumpLabels[i : i+1]
|
label = t.jumpLabels[i:i+1] + strings.Repeat(" ", t.pointerLen-1)
|
||||||
}
|
}
|
||||||
} else if current {
|
} else if current {
|
||||||
label = ">"
|
label = t.pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid unnecessary redraw
|
// Avoid unnecessary redraw
|
||||||
@@ -875,17 +962,17 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool) {
|
|||||||
if current {
|
if current {
|
||||||
t.window.CPrint(tui.ColCurrentCursor, t.strong, label)
|
t.window.CPrint(tui.ColCurrentCursor, t.strong, label)
|
||||||
if selected {
|
if selected {
|
||||||
t.window.CPrint(tui.ColCurrentSelected, t.strong, ">")
|
t.window.CPrint(tui.ColCurrentSelected, t.strong, t.marker)
|
||||||
} else {
|
} else {
|
||||||
t.window.CPrint(tui.ColCurrentSelected, t.strong, " ")
|
t.window.CPrint(tui.ColCurrentSelected, t.strong, t.markerEmpty)
|
||||||
}
|
}
|
||||||
newLine.width = t.printHighlighted(result, t.strong, tui.ColCurrent, tui.ColCurrentMatch, true, true)
|
newLine.width = t.printHighlighted(result, t.strong, tui.ColCurrent, tui.ColCurrentMatch, true, true)
|
||||||
} else {
|
} else {
|
||||||
t.window.CPrint(tui.ColCursor, t.strong, label)
|
t.window.CPrint(tui.ColCursor, t.strong, label)
|
||||||
if selected {
|
if selected {
|
||||||
t.window.CPrint(tui.ColSelected, t.strong, ">")
|
t.window.CPrint(tui.ColSelected, t.strong, t.marker)
|
||||||
} else {
|
} else {
|
||||||
t.window.Print(" ")
|
t.window.Print(t.markerEmpty)
|
||||||
}
|
}
|
||||||
newLine.width = t.printHighlighted(result, 0, tui.ColNormal, tui.ColMatch, false, true)
|
newLine.width = t.printHighlighted(result, 0, tui.ColNormal, tui.ColMatch, false, true)
|
||||||
}
|
}
|
||||||
@@ -967,25 +1054,11 @@ func (t *Terminal) printHighlighted(result Result, attr tui.Attr, col1 tui.Color
|
|||||||
}
|
}
|
||||||
|
|
||||||
offsets := result.colorOffsets(charOffsets, t.theme, col2, attr, current)
|
offsets := result.colorOffsets(charOffsets, t.theme, col2, attr, current)
|
||||||
maxWidth := t.window.Width() - 3
|
maxWidth := t.window.Width() - (t.pointerLen + t.markerLen + 1)
|
||||||
maxe = util.Constrain(maxe+util.Min(maxWidth/2-2, t.hscrollOff), 0, len(text))
|
maxe = util.Constrain(maxe+util.Min(maxWidth/2-2, t.hscrollOff), 0, len(text))
|
||||||
displayWidth := t.displayWidthWithLimit(text, 0, maxWidth)
|
displayWidth := t.displayWidthWithLimit(text, 0, maxWidth)
|
||||||
if displayWidth > maxWidth {
|
if displayWidth > maxWidth {
|
||||||
if t.hscroll {
|
transformOffsets := func(diff int32) {
|
||||||
// Stri..
|
|
||||||
if !t.overflow(text[:maxe], maxWidth-2) {
|
|
||||||
text, _ = t.trimRight(text, maxWidth-2)
|
|
||||||
text = append(text, []rune("..")...)
|
|
||||||
} else {
|
|
||||||
// Stri..
|
|
||||||
if t.overflow(text[maxe:], 2) {
|
|
||||||
text = append(text[:maxe], []rune("..")...)
|
|
||||||
}
|
|
||||||
// ..ri..
|
|
||||||
var diff int32
|
|
||||||
text, diff = t.trimLeft(text, maxWidth-2)
|
|
||||||
|
|
||||||
// Transform offsets
|
|
||||||
for idx, offset := range offsets {
|
for idx, offset := range offsets {
|
||||||
b, e := offset.offset[0], offset.offset[1]
|
b, e := offset.offset[0], offset.offset[1]
|
||||||
b += 2 - diff
|
b += 2 - diff
|
||||||
@@ -994,11 +1067,32 @@ func (t *Terminal) printHighlighted(result Result, attr tui.Attr, col1 tui.Color
|
|||||||
offsets[idx].offset[0] = b
|
offsets[idx].offset[0] = b
|
||||||
offsets[idx].offset[1] = util.Max32(b, e)
|
offsets[idx].offset[1] = util.Max32(b, e)
|
||||||
}
|
}
|
||||||
text = append([]rune(".."), text...)
|
}
|
||||||
|
if t.hscroll {
|
||||||
|
if t.keepRight && pos == nil {
|
||||||
|
trimmed, diff := t.trimLeft(text, maxWidth-2)
|
||||||
|
transformOffsets(diff)
|
||||||
|
text = append([]rune(ellipsis), trimmed...)
|
||||||
|
} else if !t.overflow(text[:maxe], maxWidth-2) {
|
||||||
|
// Stri..
|
||||||
|
text, _ = t.trimRight(text, maxWidth-2)
|
||||||
|
text = append(text, []rune(ellipsis)...)
|
||||||
|
} else {
|
||||||
|
// Stri..
|
||||||
|
if t.overflow(text[maxe:], 2) {
|
||||||
|
text = append(text[:maxe], []rune(ellipsis)...)
|
||||||
|
}
|
||||||
|
// ..ri..
|
||||||
|
var diff int32
|
||||||
|
text, diff = t.trimLeft(text, maxWidth-2)
|
||||||
|
|
||||||
|
// Transform offsets
|
||||||
|
transformOffsets(diff)
|
||||||
|
text = append([]rune(ellipsis), text...)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
text, _ = t.trimRight(text, maxWidth-2)
|
text, _ = t.trimRight(text, maxWidth-2)
|
||||||
text = append(text, []rune("..")...)
|
text = append(text, []rune(ellipsis)...)
|
||||||
|
|
||||||
for idx, offset := range offsets {
|
for idx, offset := range offsets {
|
||||||
offsets[idx].offset[0] = util.Min32(offset.offset[0], int32(maxWidth-2))
|
offsets[idx].offset[0] = util.Min32(offset.offset[0], int32(maxWidth-2))
|
||||||
@@ -1129,11 +1223,14 @@ func (t *Terminal) refresh() {
|
|||||||
t.placeCursor()
|
t.placeCursor()
|
||||||
if !t.suppress {
|
if !t.suppress {
|
||||||
windows := make([]tui.Window, 0, 4)
|
windows := make([]tui.Window, 0, 4)
|
||||||
if t.bordered {
|
if t.borderShape != tui.BorderNone {
|
||||||
windows = append(windows, t.border)
|
windows = append(windows, t.border)
|
||||||
}
|
}
|
||||||
if t.hasPreviewWindow() {
|
if t.hasPreviewWindow() {
|
||||||
windows = append(windows, t.pborder, t.pwindow)
|
if t.pborder != nil {
|
||||||
|
windows = append(windows, t.pborder)
|
||||||
|
}
|
||||||
|
windows = append(windows, t.pwindow)
|
||||||
}
|
}
|
||||||
windows = append(windows, t.window)
|
windows = append(windows, t.window)
|
||||||
t.tui.RefreshWindows(windows)
|
t.tui.RefreshWindows(windows)
|
||||||
@@ -1157,7 +1254,8 @@ func findLastMatch(pattern string, str string) int {
|
|||||||
if locs == nil {
|
if locs == nil {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
return locs[len(locs)-1][0]
|
prefix := []rune(str[:locs[len(locs)-1][0]])
|
||||||
|
return len(prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func findFirstMatch(pattern string, str string) int {
|
func findFirstMatch(pattern string, str string) int {
|
||||||
@@ -1169,7 +1267,8 @@ func findFirstMatch(pattern string, str string) int {
|
|||||||
if loc == nil {
|
if loc == nil {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
return loc[0]
|
prefix := []rune(str[:loc[0]])
|
||||||
|
return len(prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func copySlice(slice []rune) []rune {
|
func copySlice(slice []rune) []rune {
|
||||||
@@ -1277,6 +1376,50 @@ func cleanTemporaryFiles() {
|
|||||||
activeTempFiles = []string{}
|
activeTempFiles = []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) replacePlaceholder(template string, forcePlus bool, input string, list []*Item) string {
|
||||||
|
return replacePlaceholder(
|
||||||
|
template, t.ansi, t.delimiter, t.printsep, forcePlus, input, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ascii to positive integer
|
||||||
|
func atopi(s string) int {
|
||||||
|
matches := numericPrefix.FindStringSubmatch(s)
|
||||||
|
if len(matches) < 2 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
n, e := strconv.Atoi(matches[1])
|
||||||
|
if e != nil || n < 1 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) evaluateScrollOffset(list []*Item, height int) int {
|
||||||
|
offsetExpr := t.replacePlaceholder(t.preview.scroll, false, "", list)
|
||||||
|
nums := strings.Split(offsetExpr, "-")
|
||||||
|
switch len(nums) {
|
||||||
|
case 0:
|
||||||
|
return 0
|
||||||
|
case 1, 2:
|
||||||
|
base := atopi(nums[0])
|
||||||
|
if base == 0 {
|
||||||
|
return 0
|
||||||
|
} else if len(nums) == 1 {
|
||||||
|
return base - 1
|
||||||
|
}
|
||||||
|
if nums[1][0] == '/' {
|
||||||
|
denom := atopi(nums[1][1:])
|
||||||
|
if denom == 0 {
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
return base - height/denom
|
||||||
|
}
|
||||||
|
return base - atopi(nums[1]) - 1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, printsep string, forcePlus bool, query string, allItems []*Item) string {
|
func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, printsep string, forcePlus bool, query string, allItems []*Item) string {
|
||||||
current := allItems[:1]
|
current := allItems[:1]
|
||||||
selected := allItems[1:]
|
selected := allItems[1:]
|
||||||
@@ -1375,7 +1518,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
|
|||||||
if !valid {
|
if !valid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
command := replacePlaceholder(template, t.ansi, t.delimiter, t.printsep, forcePlus, string(t.input), list)
|
command := t.replacePlaceholder(template, forcePlus, string(t.input), list)
|
||||||
cmd := util.ExecCommand(command, false)
|
cmd := util.ExecCommand(command, false)
|
||||||
if !background {
|
if !background {
|
||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
@@ -1383,13 +1526,13 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
|
|||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
t.tui.Pause(true)
|
t.tui.Pause(true)
|
||||||
cmd.Run()
|
cmd.Run()
|
||||||
t.tui.Resume(true)
|
t.tui.Resume(true, false)
|
||||||
t.redraw()
|
t.redraw()
|
||||||
t.refresh()
|
t.refresh()
|
||||||
} else {
|
} else {
|
||||||
t.tui.Pause(false)
|
t.tui.Pause(false)
|
||||||
cmd.Run()
|
cmd.Run()
|
||||||
t.tui.Resume(false)
|
t.tui.Resume(false, false)
|
||||||
}
|
}
|
||||||
cleanTemporaryFiles()
|
cleanTemporaryFiles()
|
||||||
}
|
}
|
||||||
@@ -1416,8 +1559,8 @@ func (t *Terminal) currentItem() *Item {
|
|||||||
|
|
||||||
func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, []*Item) {
|
func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, []*Item) {
|
||||||
current := t.currentItem()
|
current := t.currentItem()
|
||||||
_, plus, query := hasPreviewFlags(template)
|
slot, plus, query := hasPreviewFlags(template)
|
||||||
if !(query && len(t.input) > 0 || (forcePlus || plus) && len(t.selected) > 0) {
|
if !(!slot || query && len(t.input) > 0 || (forcePlus || plus) && len(t.selected) > 0) {
|
||||||
return current != nil, []*Item{current, current}
|
return current != nil, []*Item{current, current}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1544,24 +1687,29 @@ func (t *Terminal) Loop() {
|
|||||||
if t.hasPreviewer() {
|
if t.hasPreviewer() {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
var request []*Item
|
var items []*Item
|
||||||
|
var commandTemplate string
|
||||||
t.previewBox.Wait(func(events *util.Events) {
|
t.previewBox.Wait(func(events *util.Events) {
|
||||||
for req, value := range *events {
|
for req, value := range *events {
|
||||||
switch req {
|
switch req {
|
||||||
case reqPreviewEnqueue:
|
case reqPreviewEnqueue:
|
||||||
request = value.([]*Item)
|
request := value.(previewRequest)
|
||||||
|
commandTemplate = request.template
|
||||||
|
items = request.list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
events.Clear()
|
events.Clear()
|
||||||
})
|
})
|
||||||
// We don't display preview window if no match
|
// We don't display preview window if no match
|
||||||
if request[0] != nil {
|
if items[0] != nil {
|
||||||
command := replacePlaceholder(t.preview.command,
|
command := t.replacePlaceholder(commandTemplate, false, string(t.Input()), items)
|
||||||
t.ansi, t.delimiter, t.printsep, false, string(t.Input()), request)
|
offset := 0
|
||||||
cmd := util.ExecCommand(command, true)
|
cmd := util.ExecCommand(command, true)
|
||||||
if t.pwindow != nil {
|
if t.pwindow != nil {
|
||||||
|
height := t.pwindow.Height()
|
||||||
|
offset = t.evaluateScrollOffset(items, height)
|
||||||
env := os.Environ()
|
env := os.Environ()
|
||||||
lines := fmt.Sprintf("LINES=%d", t.pwindow.Height())
|
lines := fmt.Sprintf("LINES=%d", height)
|
||||||
columns := fmt.Sprintf("COLUMNS=%d", t.pwindow.Width())
|
columns := fmt.Sprintf("COLUMNS=%d", t.pwindow.Width())
|
||||||
env = append(env, lines)
|
env = append(env, lines)
|
||||||
env = append(env, "FZF_PREVIEW_"+lines)
|
env = append(env, "FZF_PREVIEW_"+lines)
|
||||||
@@ -1572,7 +1720,10 @@ func (t *Terminal) Loop() {
|
|||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
cmd.Stdout = &out
|
cmd.Stdout = &out
|
||||||
cmd.Stderr = &out
|
cmd.Stderr = &out
|
||||||
cmd.Start()
|
err := cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
out.Write([]byte(err.Error()))
|
||||||
|
}
|
||||||
finishChan := make(chan bool, 1)
|
finishChan := make(chan bool, 1)
|
||||||
updateChan := make(chan bool)
|
updateChan := make(chan bool)
|
||||||
go func() {
|
go func() {
|
||||||
@@ -1597,11 +1748,11 @@ func (t *Terminal) Loop() {
|
|||||||
cmd.Wait()
|
cmd.Wait()
|
||||||
finishChan <- true
|
finishChan <- true
|
||||||
if out.Len() > 0 || !<-updateChan {
|
if out.Len() > 0 || !<-updateChan {
|
||||||
t.reqBox.Set(reqPreviewDisplay, out.String())
|
t.reqBox.Set(reqPreviewDisplay, previewResult{out.String(), offset})
|
||||||
}
|
}
|
||||||
cleanTemporaryFiles()
|
cleanTemporaryFiles()
|
||||||
} else {
|
} else {
|
||||||
t.reqBox.Set(reqPreviewDisplay, "")
|
t.reqBox.Set(reqPreviewDisplay, previewResult{"", 0})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -1617,6 +1768,14 @@ func (t *Terminal) Loop() {
|
|||||||
t.killPreview(code)
|
t.killPreview(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshPreview := func(command string) {
|
||||||
|
if len(command) > 0 && t.isPreviewEnabled() {
|
||||||
|
_, list := t.buildPlusList(command, false)
|
||||||
|
t.cancelPreview()
|
||||||
|
t.previewBox.Set(reqPreviewEnqueue, previewRequest{command, list})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
var focusedIndex int32 = minItem.Index()
|
var focusedIndex int32 = minItem.Index()
|
||||||
var version int64 = -1
|
var version int64 = -1
|
||||||
@@ -1643,11 +1802,7 @@ func (t *Terminal) Loop() {
|
|||||||
if focusedIndex != currentIndex || version != t.version {
|
if focusedIndex != currentIndex || version != t.version {
|
||||||
version = t.version
|
version = t.version
|
||||||
focusedIndex = currentIndex
|
focusedIndex = currentIndex
|
||||||
if t.isPreviewEnabled() {
|
refreshPreview(t.preview.command)
|
||||||
_, list := t.buildPlusList(t.preview.command, false)
|
|
||||||
t.cancelPreview()
|
|
||||||
t.previewBox.Set(reqPreviewEnqueue, list)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case reqJump:
|
case reqJump:
|
||||||
if t.merger.Length() == 0 {
|
if t.merger.Length() == 0 {
|
||||||
@@ -1659,7 +1814,7 @@ func (t *Terminal) Loop() {
|
|||||||
case reqRefresh:
|
case reqRefresh:
|
||||||
t.suppress = false
|
t.suppress = false
|
||||||
case reqReinit:
|
case reqReinit:
|
||||||
t.tui.Resume(t.fullscreen)
|
t.tui.Resume(t.fullscreen, true)
|
||||||
t.redraw()
|
t.redraw()
|
||||||
case reqRedraw:
|
case reqRedraw:
|
||||||
t.redraw()
|
t.redraw()
|
||||||
@@ -1671,9 +1826,10 @@ func (t *Terminal) Loop() {
|
|||||||
return exitNoMatch
|
return exitNoMatch
|
||||||
})
|
})
|
||||||
case reqPreviewDisplay:
|
case reqPreviewDisplay:
|
||||||
t.previewer.text = value.(string)
|
result := value.(previewResult)
|
||||||
|
t.previewer.text = result.content
|
||||||
t.previewer.lines = strings.Count(t.previewer.text, "\n")
|
t.previewer.lines = strings.Count(t.previewer.text, "\n")
|
||||||
t.previewer.offset = 0
|
t.previewer.offset = util.Constrain(result.offset, 0, t.previewer.lines-1)
|
||||||
t.printPreview()
|
t.printPreview()
|
||||||
case reqPreviewRefresh:
|
case reqPreviewRefresh:
|
||||||
t.printPreview()
|
t.printPreview()
|
||||||
@@ -1696,6 +1852,7 @@ func (t *Terminal) Loop() {
|
|||||||
for looping {
|
for looping {
|
||||||
var newCommand *string
|
var newCommand *string
|
||||||
changed := false
|
changed := false
|
||||||
|
beof := false
|
||||||
queryChanged := false
|
queryChanged := false
|
||||||
|
|
||||||
event := t.tui.GetChar()
|
event := t.tui.GetChar()
|
||||||
@@ -1712,6 +1869,14 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
togglePreview := func(enabled bool) {
|
||||||
|
if t.previewer.enabled != enabled {
|
||||||
|
t.previewer.enabled = enabled
|
||||||
|
t.tui.Clear()
|
||||||
|
t.resizeWindows()
|
||||||
|
req(reqPrompt, reqList, reqInfo, reqHeader)
|
||||||
|
}
|
||||||
|
}
|
||||||
toggle := func() bool {
|
toggle := func() bool {
|
||||||
if t.cy < t.merger.Length() && t.toggleItem(t.merger.Get(t.cy).item) {
|
if t.cy < t.merger.Length() && t.toggleItem(t.merger.Get(t.cy).item) {
|
||||||
req(reqInfo)
|
req(reqInfo)
|
||||||
@@ -1723,8 +1888,11 @@ func (t *Terminal) Loop() {
|
|||||||
if !t.previewer.more {
|
if !t.previewer.more {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
newOffset := util.Constrain(
|
newOffset := t.previewer.offset + amount
|
||||||
t.previewer.offset+amount, 0, t.previewer.lines-1)
|
if t.preview.cycle {
|
||||||
|
newOffset = (newOffset + t.previewer.lines) % t.previewer.lines
|
||||||
|
}
|
||||||
|
newOffset = util.Constrain(newOffset, 0, t.previewer.lines-1)
|
||||||
if t.previewer.offset != newOffset {
|
if t.previewer.offset != newOffset {
|
||||||
t.previewer.offset = newOffset
|
t.previewer.offset = newOffset
|
||||||
req(reqPreviewRefresh)
|
req(reqPreviewRefresh)
|
||||||
@@ -1760,17 +1928,15 @@ func (t *Terminal) Loop() {
|
|||||||
return false
|
return false
|
||||||
case actTogglePreview:
|
case actTogglePreview:
|
||||||
if t.hasPreviewer() {
|
if t.hasPreviewer() {
|
||||||
t.previewer.enabled = !t.previewer.enabled
|
togglePreview(!t.previewer.enabled)
|
||||||
t.tui.Clear()
|
|
||||||
t.resizeWindows()
|
|
||||||
if t.previewer.enabled {
|
if t.previewer.enabled {
|
||||||
valid, list := t.buildPlusList(t.preview.command, false)
|
valid, list := t.buildPlusList(t.preview.command, false)
|
||||||
if valid {
|
if valid {
|
||||||
t.cancelPreview()
|
t.cancelPreview()
|
||||||
t.previewBox.Set(reqPreviewEnqueue, list)
|
t.previewBox.Set(reqPreviewEnqueue,
|
||||||
|
previewRequest{t.preview.command, list})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
req(reqPrompt, reqList, reqInfo, reqHeader)
|
|
||||||
}
|
}
|
||||||
case actTogglePreviewWrap:
|
case actTogglePreviewWrap:
|
||||||
if t.hasPreviewWindow() {
|
if t.hasPreviewWindow() {
|
||||||
@@ -1796,6 +1962,14 @@ func (t *Terminal) Loop() {
|
|||||||
if t.hasPreviewWindow() {
|
if t.hasPreviewWindow() {
|
||||||
scrollPreview(t.pwindow.Height())
|
scrollPreview(t.pwindow.Height())
|
||||||
}
|
}
|
||||||
|
case actPreviewHalfPageUp:
|
||||||
|
if t.hasPreviewWindow() {
|
||||||
|
scrollPreview(-t.pwindow.Height() / 2)
|
||||||
|
}
|
||||||
|
case actPreviewHalfPageDown:
|
||||||
|
if t.hasPreviewWindow() {
|
||||||
|
scrollPreview(t.pwindow.Height() / 2)
|
||||||
|
}
|
||||||
case actBeginningOfLine:
|
case actBeginningOfLine:
|
||||||
t.cx = 0
|
t.cx = 0
|
||||||
case actBackwardChar:
|
case actBackwardChar:
|
||||||
@@ -1804,6 +1978,11 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
case actPrintQuery:
|
case actPrintQuery:
|
||||||
req(reqPrintQuery)
|
req(reqPrintQuery)
|
||||||
|
case actPreview:
|
||||||
|
togglePreview(true)
|
||||||
|
refreshPreview(a.a)
|
||||||
|
case actRefreshPreview:
|
||||||
|
refreshPreview(t.preview.command)
|
||||||
case actReplaceQuery:
|
case actReplaceQuery:
|
||||||
if t.cy >= 0 && t.cy < t.merger.Length() {
|
if t.cy >= 0 && t.cy < t.merger.Length() {
|
||||||
t.input = t.merger.Get(t.cy).item.text.ToRunes()
|
t.input = t.merger.Get(t.cy).item.text.ToRunes()
|
||||||
@@ -1827,11 +2006,19 @@ func (t *Terminal) Loop() {
|
|||||||
t.input = []rune{}
|
t.input = []rune{}
|
||||||
t.cx = 0
|
t.cx = 0
|
||||||
}
|
}
|
||||||
|
case actBackwardDeleteCharEOF:
|
||||||
|
if len(t.input) == 0 {
|
||||||
|
req(reqQuit)
|
||||||
|
} else if t.cx > 0 {
|
||||||
|
t.input = append(t.input[:t.cx-1], t.input[t.cx:]...)
|
||||||
|
t.cx--
|
||||||
|
}
|
||||||
case actForwardChar:
|
case actForwardChar:
|
||||||
if t.cx < len(t.input) {
|
if t.cx < len(t.input) {
|
||||||
t.cx++
|
t.cx++
|
||||||
}
|
}
|
||||||
case actBackwardDeleteChar:
|
case actBackwardDeleteChar:
|
||||||
|
beof = len(t.input) == 0
|
||||||
if t.cx > 0 {
|
if t.cx > 0 {
|
||||||
t.input = append(t.input[:t.cx-1], t.input[t.cx:]...)
|
t.input = append(t.input[:t.cx-1], t.input[t.cx:]...)
|
||||||
t.cx--
|
t.cx--
|
||||||
@@ -1924,16 +2111,19 @@ func (t *Terminal) Loop() {
|
|||||||
t.vset(0)
|
t.vset(0)
|
||||||
req(reqList)
|
req(reqList)
|
||||||
case actUnixLineDiscard:
|
case actUnixLineDiscard:
|
||||||
|
beof = len(t.input) == 0
|
||||||
if t.cx > 0 {
|
if t.cx > 0 {
|
||||||
t.yanked = copySlice(t.input[:t.cx])
|
t.yanked = copySlice(t.input[:t.cx])
|
||||||
t.input = t.input[t.cx:]
|
t.input = t.input[t.cx:]
|
||||||
t.cx = 0
|
t.cx = 0
|
||||||
}
|
}
|
||||||
case actUnixWordRubout:
|
case actUnixWordRubout:
|
||||||
|
beof = len(t.input) == 0
|
||||||
if t.cx > 0 {
|
if t.cx > 0 {
|
||||||
t.rubout("\\s\\S")
|
t.rubout("\\s\\S")
|
||||||
}
|
}
|
||||||
case actBackwardKillWord:
|
case actBackwardKillWord:
|
||||||
|
beof = len(t.input) == 0
|
||||||
if t.cx > 0 {
|
if t.cx > 0 {
|
||||||
t.rubout(t.wordRubout)
|
t.rubout(t.wordRubout)
|
||||||
}
|
}
|
||||||
@@ -2069,8 +2259,7 @@ func (t *Terminal) Loop() {
|
|||||||
valid = !slot || query
|
valid = !slot || query
|
||||||
}
|
}
|
||||||
if valid {
|
if valid {
|
||||||
command := replacePlaceholder(a.a,
|
command := t.replacePlaceholder(a.a, false, string(t.input), list)
|
||||||
t.ansi, t.delimiter, t.printsep, false, string(t.input), list)
|
|
||||||
newCommand = &command
|
newCommand = &command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2096,6 +2285,11 @@ func (t *Terminal) Loop() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if onEOFs, prs := t.keymap[tui.BackwardEOF]; beof && prs {
|
||||||
|
if !doActions(onEOFs, tui.BackwardEOF) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if mapkey == tui.Rune {
|
if mapkey == 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() {
|
||||||
|
@@ -27,7 +27,7 @@ const (
|
|||||||
|
|
||||||
func (r *FullscreenRenderer) Init() {}
|
func (r *FullscreenRenderer) Init() {}
|
||||||
func (r *FullscreenRenderer) Pause(bool) {}
|
func (r *FullscreenRenderer) Pause(bool) {}
|
||||||
func (r *FullscreenRenderer) Resume(bool) {}
|
func (r *FullscreenRenderer) Resume(bool, bool) {}
|
||||||
func (r *FullscreenRenderer) Clear() {}
|
func (r *FullscreenRenderer) Clear() {}
|
||||||
func (r *FullscreenRenderer) Refresh() {}
|
func (r *FullscreenRenderer) Refresh() {}
|
||||||
func (r *FullscreenRenderer) Close() {}
|
func (r *FullscreenRenderer) Close() {}
|
||||||
|
221
src/tui/light.go
221
src/tui/light.go
@@ -3,11 +3,9 @@ package tui
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
@@ -23,26 +21,13 @@ const (
|
|||||||
defaultEscDelay = 100
|
defaultEscDelay = 100
|
||||||
escPollInterval = 5
|
escPollInterval = 5
|
||||||
offsetPollTries = 10
|
offsetPollTries = 10
|
||||||
|
maxInputBuffer = 10 * 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
const consoleDevice string = "/dev/tty"
|
const consoleDevice string = "/dev/tty"
|
||||||
|
|
||||||
var offsetRegexp *regexp.Regexp = regexp.MustCompile("(.*)\x1b\\[([0-9]+);([0-9]+)R")
|
var offsetRegexp *regexp.Regexp = regexp.MustCompile("(.*)\x1b\\[([0-9]+);([0-9]+)R")
|
||||||
|
var offsetRegexpBegin *regexp.Regexp = regexp.MustCompile("^\x1b\\[[0-9]+;[0-9]+R")
|
||||||
func openTtyIn() *os.File {
|
|
||||||
in, err := os.OpenFile(consoleDevice, syscall.O_RDONLY, 0)
|
|
||||||
if err != nil {
|
|
||||||
tty := ttyname()
|
|
||||||
if len(tty) > 0 {
|
|
||||||
if in, err := os.OpenFile(tty, syscall.O_RDONLY, 0); err == nil {
|
|
||||||
return in
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintln(os.Stderr, "Failed to open "+consoleDevice)
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
return in
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *LightRenderer) stderr(str string) {
|
func (r *LightRenderer) stderr(str string) {
|
||||||
r.stderrInternal(str, true)
|
r.stderrInternal(str, true)
|
||||||
@@ -100,11 +85,19 @@ type LightRenderer struct {
|
|||||||
y int
|
y int
|
||||||
x int
|
x int
|
||||||
maxHeightFunc func(int) int
|
maxHeightFunc func(int) int
|
||||||
|
|
||||||
|
// Windows only
|
||||||
|
ttyinChannel chan byte
|
||||||
|
inHandle uintptr
|
||||||
|
outHandle uintptr
|
||||||
|
origStateInput uint32
|
||||||
|
origStateOutput uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type LightWindow struct {
|
type LightWindow struct {
|
||||||
renderer *LightRenderer
|
renderer *LightRenderer
|
||||||
colored bool
|
colored bool
|
||||||
|
preview bool
|
||||||
border BorderStyle
|
border BorderStyle
|
||||||
top int
|
top int
|
||||||
left int
|
left int
|
||||||
@@ -132,37 +125,6 @@ func NewLightRenderer(theme *ColorTheme, forceBlack bool, mouse bool, tabstop in
|
|||||||
return &r
|
return &r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LightRenderer) fd() int {
|
|
||||||
return int(r.ttyin.Fd())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *LightRenderer) defaultTheme() *ColorTheme {
|
|
||||||
if strings.Contains(os.Getenv("TERM"), "256") {
|
|
||||||
return Dark256
|
|
||||||
}
|
|
||||||
colors, err := exec.Command("tput", "colors").Output()
|
|
||||||
if err == nil && atoi(strings.TrimSpace(string(colors)), 16) > 16 {
|
|
||||||
return Dark256
|
|
||||||
}
|
|
||||||
return Default16
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *LightRenderer) findOffset() (row int, col int) {
|
|
||||||
r.csi("6n")
|
|
||||||
r.flush()
|
|
||||||
bytes := []byte{}
|
|
||||||
for tries := 0; tries < offsetPollTries; tries++ {
|
|
||||||
bytes = r.getBytesInternal(bytes, tries > 0)
|
|
||||||
offsets := offsetRegexp.FindSubmatch(bytes)
|
|
||||||
if len(offsets) > 3 {
|
|
||||||
// add anything we skipped over to the input buffer
|
|
||||||
r.buffer = append(r.buffer, offsets[1]...)
|
|
||||||
return atoi(string(offsets[2]), 0) - 1, atoi(string(offsets[3]), 0) - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1, -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func repeat(r rune, times int) string {
|
func repeat(r rune, times int) string {
|
||||||
if times > 0 {
|
if times > 0 {
|
||||||
return strings.Repeat(string(r), times)
|
return strings.Repeat(string(r), times)
|
||||||
@@ -181,13 +143,9 @@ func atoi(s string, defaultValue int) int {
|
|||||||
func (r *LightRenderer) Init() {
|
func (r *LightRenderer) Init() {
|
||||||
r.escDelay = atoi(os.Getenv("ESCDELAY"), defaultEscDelay)
|
r.escDelay = atoi(os.Getenv("ESCDELAY"), defaultEscDelay)
|
||||||
|
|
||||||
fd := r.fd()
|
if err := r.initPlatform(); err != nil {
|
||||||
origState, err := terminal.GetState(fd)
|
|
||||||
if err != nil {
|
|
||||||
errorExit(err.Error())
|
errorExit(err.Error())
|
||||||
}
|
}
|
||||||
r.origState = origState
|
|
||||||
terminal.MakeRaw(fd)
|
|
||||||
r.updateTerminalSize()
|
r.updateTerminalSize()
|
||||||
initTheme(r.theme, r.defaultTheme(), r.forceBlack)
|
initTheme(r.theme, r.defaultTheme(), r.forceBlack)
|
||||||
|
|
||||||
@@ -260,28 +218,6 @@ func getEnv(name string, defaultValue int) int {
|
|||||||
return atoi(env, defaultValue)
|
return atoi(env, defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LightRenderer) updateTerminalSize() {
|
|
||||||
width, height, err := terminal.GetSize(r.fd())
|
|
||||||
if err == nil {
|
|
||||||
r.width = width
|
|
||||||
r.height = r.maxHeightFunc(height)
|
|
||||||
} else {
|
|
||||||
r.width = getEnv("COLUMNS", defaultWidth)
|
|
||||||
r.height = r.maxHeightFunc(getEnv("LINES", defaultHeight))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *LightRenderer) getch(nonblock bool) (int, bool) {
|
|
||||||
b := make([]byte, 1)
|
|
||||||
fd := r.fd()
|
|
||||||
util.SetNonblock(r.ttyin, nonblock)
|
|
||||||
_, err := util.Read(fd, b)
|
|
||||||
if err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
return int(b[0]), true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *LightRenderer) getBytes() []byte {
|
func (r *LightRenderer) getBytes() []byte {
|
||||||
return r.getBytesInternal(r.buffer, false)
|
return r.getBytesInternal(r.buffer, false)
|
||||||
}
|
}
|
||||||
@@ -316,6 +252,13 @@ func (r *LightRenderer) getBytesInternal(buffer []byte, nonblock bool) []byte {
|
|||||||
}
|
}
|
||||||
buffer = append(buffer, byte(c))
|
buffer = append(buffer, byte(c))
|
||||||
pc = c
|
pc = c
|
||||||
|
|
||||||
|
// This should never happen under normal conditions,
|
||||||
|
// so terminate fzf immediately.
|
||||||
|
if len(buffer) > maxInputBuffer {
|
||||||
|
r.Close()
|
||||||
|
panic(fmt.Sprintf("Input buffer overflow (%d): %v", len(buffer), buffer))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer
|
return buffer
|
||||||
@@ -379,6 +322,13 @@ 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)
|
||||||
|
if loc != nil && loc[0] == 0 {
|
||||||
|
*sz = loc[1]
|
||||||
|
return Event{Invalid, 0, nil}
|
||||||
|
}
|
||||||
|
|
||||||
*sz = 2
|
*sz = 2
|
||||||
if r.buffer[1] >= 1 && r.buffer[1] <= 'z'-'a'+1 {
|
if r.buffer[1] >= 1 && r.buffer[1] <= 'z'-'a'+1 {
|
||||||
return Event{int(CtrlAltA + r.buffer[1] - 1), 0, nil}
|
return Event{int(CtrlAltA + r.buffer[1] - 1), 0, nil}
|
||||||
@@ -391,112 +341,123 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
|||||||
switch r.buffer[1] {
|
switch r.buffer[1] {
|
||||||
case ESC:
|
case ESC:
|
||||||
return Event{ESC, 0, nil}
|
return Event{ESC, 0, nil}
|
||||||
case 32:
|
case ' ':
|
||||||
return Event{AltSpace, 0, nil}
|
return Event{AltSpace, 0, nil}
|
||||||
case 47:
|
case '/':
|
||||||
return Event{AltSlash, 0, nil}
|
return Event{AltSlash, 0, nil}
|
||||||
case 98:
|
case 'b':
|
||||||
return Event{AltB, 0, nil}
|
return Event{AltB, 0, nil}
|
||||||
case 100:
|
case 'd':
|
||||||
return Event{AltD, 0, nil}
|
return Event{AltD, 0, nil}
|
||||||
case 102:
|
case 'f':
|
||||||
return Event{AltF, 0, nil}
|
return Event{AltF, 0, nil}
|
||||||
case 127:
|
case 127:
|
||||||
return Event{AltBS, 0, nil}
|
return Event{AltBS, 0, nil}
|
||||||
case 91, 79:
|
case '[', 'O':
|
||||||
if len(r.buffer) < 3 {
|
if len(r.buffer) < 3 {
|
||||||
return Event{Invalid, 0, nil}
|
return Event{Invalid, 0, nil}
|
||||||
}
|
}
|
||||||
*sz = 3
|
*sz = 3
|
||||||
switch r.buffer[2] {
|
switch r.buffer[2] {
|
||||||
case 68:
|
case 'D':
|
||||||
if alt {
|
if alt {
|
||||||
return Event{AltLeft, 0, nil}
|
return Event{AltLeft, 0, nil}
|
||||||
}
|
}
|
||||||
return Event{Left, 0, nil}
|
return Event{Left, 0, nil}
|
||||||
case 67:
|
case 'C':
|
||||||
if alt {
|
if alt {
|
||||||
// Ugh..
|
// Ugh..
|
||||||
return Event{AltRight, 0, nil}
|
return Event{AltRight, 0, nil}
|
||||||
}
|
}
|
||||||
return Event{Right, 0, nil}
|
return Event{Right, 0, nil}
|
||||||
case 66:
|
case 'B':
|
||||||
if alt {
|
if alt {
|
||||||
return Event{AltDown, 0, nil}
|
return Event{AltDown, 0, nil}
|
||||||
}
|
}
|
||||||
return Event{Down, 0, nil}
|
return Event{Down, 0, nil}
|
||||||
case 65:
|
case 'A':
|
||||||
if alt {
|
if alt {
|
||||||
return Event{AltUp, 0, nil}
|
return Event{AltUp, 0, nil}
|
||||||
}
|
}
|
||||||
return Event{Up, 0, nil}
|
return Event{Up, 0, nil}
|
||||||
case 90:
|
case 'Z':
|
||||||
return Event{BTab, 0, nil}
|
return Event{BTab, 0, nil}
|
||||||
case 72:
|
case 'H':
|
||||||
return Event{Home, 0, nil}
|
return Event{Home, 0, nil}
|
||||||
case 70:
|
case 'F':
|
||||||
return Event{End, 0, nil}
|
return Event{End, 0, nil}
|
||||||
case 77:
|
case 'M':
|
||||||
return r.mouseSequence(sz)
|
return r.mouseSequence(sz)
|
||||||
case 80:
|
case 'P':
|
||||||
return Event{F1, 0, nil}
|
return Event{F1, 0, nil}
|
||||||
case 81:
|
case 'Q':
|
||||||
return Event{F2, 0, nil}
|
return Event{F2, 0, nil}
|
||||||
case 82:
|
case 'R':
|
||||||
return Event{F3, 0, nil}
|
return Event{F3, 0, nil}
|
||||||
case 83:
|
case 'S':
|
||||||
return Event{F4, 0, nil}
|
return Event{F4, 0, nil}
|
||||||
case 49, 50, 51, 52, 53, 54:
|
case '1', '2', '3', '4', '5', '6':
|
||||||
if len(r.buffer) < 4 {
|
if len(r.buffer) < 4 {
|
||||||
return Event{Invalid, 0, nil}
|
return Event{Invalid, 0, nil}
|
||||||
}
|
}
|
||||||
*sz = 4
|
*sz = 4
|
||||||
switch r.buffer[2] {
|
switch r.buffer[2] {
|
||||||
case 50:
|
case '2':
|
||||||
if len(r.buffer) == 5 && r.buffer[4] == 126 {
|
if r.buffer[3] == '~' {
|
||||||
|
return Event{Insert, 0, nil}
|
||||||
|
}
|
||||||
|
if len(r.buffer) > 4 && r.buffer[4] == '~' {
|
||||||
*sz = 5
|
*sz = 5
|
||||||
switch r.buffer[3] {
|
switch r.buffer[3] {
|
||||||
case 48:
|
case '0':
|
||||||
return Event{F9, 0, nil}
|
return Event{F9, 0, nil}
|
||||||
case 49:
|
case '1':
|
||||||
return Event{F10, 0, nil}
|
return Event{F10, 0, nil}
|
||||||
case 51:
|
case '3':
|
||||||
return Event{F11, 0, nil}
|
return Event{F11, 0, nil}
|
||||||
case 52:
|
case '4':
|
||||||
return Event{F12, 0, nil}
|
return Event{F12, 0, nil}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Bracketed paste mode: \e[200~ ... \e[201~
|
// Bracketed paste mode: \e[200~ ... \e[201~
|
||||||
if r.buffer[3] == '0' && (r.buffer[4] == '0' || r.buffer[4] == '1') && r.buffer[5] == '~' {
|
if len(r.buffer) > 5 && r.buffer[3] == '0' && (r.buffer[4] == '0' || r.buffer[4] == '1') && r.buffer[5] == '~' {
|
||||||
// Immediately discard the sequence from the buffer and reread input
|
// Immediately discard the sequence from the buffer and reread input
|
||||||
r.buffer = r.buffer[6:]
|
r.buffer = r.buffer[6:]
|
||||||
*sz = 0
|
*sz = 0
|
||||||
return r.GetChar()
|
return r.GetChar()
|
||||||
}
|
}
|
||||||
return Event{Invalid, 0, nil} // INS
|
return Event{Invalid, 0, nil} // INS
|
||||||
case 51:
|
case '3':
|
||||||
return Event{Del, 0, nil}
|
return Event{Del, 0, nil}
|
||||||
case 52:
|
case '4':
|
||||||
return Event{End, 0, nil}
|
return Event{End, 0, nil}
|
||||||
case 53:
|
case '5':
|
||||||
return Event{PgUp, 0, nil}
|
return Event{PgUp, 0, nil}
|
||||||
case 54:
|
case '6':
|
||||||
return Event{PgDn, 0, nil}
|
return Event{PgDn, 0, nil}
|
||||||
case 49:
|
case '1':
|
||||||
switch r.buffer[3] {
|
switch r.buffer[3] {
|
||||||
case 126:
|
case '~':
|
||||||
return Event{Home, 0, nil}
|
return Event{Home, 0, nil}
|
||||||
case 53, 55, 56, 57:
|
case '1', '2', '3', '4', '5', '7', '8', '9':
|
||||||
if len(r.buffer) == 5 && r.buffer[4] == 126 {
|
if len(r.buffer) == 5 && r.buffer[4] == '~' {
|
||||||
*sz = 5
|
*sz = 5
|
||||||
switch r.buffer[3] {
|
switch r.buffer[3] {
|
||||||
case 53:
|
case '1':
|
||||||
|
return Event{F1, 0, nil}
|
||||||
|
case '2':
|
||||||
|
return Event{F2, 0, nil}
|
||||||
|
case '3':
|
||||||
|
return Event{F3, 0, nil}
|
||||||
|
case '4':
|
||||||
|
return Event{F4, 0, nil}
|
||||||
|
case '5':
|
||||||
return Event{F5, 0, nil}
|
return Event{F5, 0, nil}
|
||||||
case 55:
|
case '7':
|
||||||
return Event{F6, 0, nil}
|
return Event{F6, 0, nil}
|
||||||
case 56:
|
case '8':
|
||||||
return Event{F7, 0, nil}
|
return Event{F7, 0, nil}
|
||||||
case 57:
|
case '9':
|
||||||
return Event{F8, 0, nil}
|
return Event{F8, 0, nil}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -584,7 +545,7 @@ func (r *LightRenderer) rmcup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *LightRenderer) Pause(clear bool) {
|
func (r *LightRenderer) Pause(clear bool) {
|
||||||
terminal.Restore(r.fd(), r.origState)
|
r.restoreTerminal()
|
||||||
if clear {
|
if clear {
|
||||||
if r.fullscreen {
|
if r.fullscreen {
|
||||||
r.rmcup()
|
r.rmcup()
|
||||||
@@ -596,8 +557,8 @@ func (r *LightRenderer) Pause(clear bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LightRenderer) Resume(clear bool) {
|
func (r *LightRenderer) Resume(clear bool, sigcont bool) {
|
||||||
terminal.MakeRaw(r.fd())
|
r.setupTerminal()
|
||||||
if clear {
|
if clear {
|
||||||
if r.fullscreen {
|
if r.fullscreen {
|
||||||
r.smcup()
|
r.smcup()
|
||||||
@@ -605,10 +566,10 @@ func (r *LightRenderer) Resume(clear bool) {
|
|||||||
r.rmcup()
|
r.rmcup()
|
||||||
}
|
}
|
||||||
r.flush()
|
r.flush()
|
||||||
} else if !r.fullscreen && r.mouse {
|
} else if sigcont && !r.fullscreen && r.mouse {
|
||||||
// NOTE: Resume(false) is only called on SIGCONT after SIGSTOP.
|
// NOTE: SIGCONT (Coming back from CTRL-Z):
|
||||||
// And It's highly likely that the offset we obtained at the beginning will
|
// It's highly likely that the offset we obtained at the beginning is
|
||||||
// no longer be correct, so we simply disable mouse input.
|
// no longer correct, so we simply disable mouse input.
|
||||||
r.csi("?1000l")
|
r.csi("?1000l")
|
||||||
r.mouse = false
|
r.mouse = false
|
||||||
}
|
}
|
||||||
@@ -651,7 +612,8 @@ func (r *LightRenderer) Close() {
|
|||||||
r.csi("?1000l")
|
r.csi("?1000l")
|
||||||
}
|
}
|
||||||
r.flush()
|
r.flush()
|
||||||
terminal.Restore(r.fd(), r.origState)
|
r.closePlatform()
|
||||||
|
r.restoreTerminal()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LightRenderer) MaxX() int {
|
func (r *LightRenderer) MaxX() int {
|
||||||
@@ -670,6 +632,7 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, prev
|
|||||||
w := &LightWindow{
|
w := &LightWindow{
|
||||||
renderer: r,
|
renderer: r,
|
||||||
colored: r.theme != nil,
|
colored: r.theme != nil,
|
||||||
|
preview: preview,
|
||||||
border: borderStyle,
|
border: borderStyle,
|
||||||
top: top,
|
top: top,
|
||||||
left: left,
|
left: left,
|
||||||
@@ -693,7 +656,7 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, prev
|
|||||||
|
|
||||||
func (w *LightWindow) drawBorder() {
|
func (w *LightWindow) drawBorder() {
|
||||||
switch w.border.shape {
|
switch w.border.shape {
|
||||||
case BorderAround:
|
case BorderRounded, BorderSharp:
|
||||||
w.drawBorderAround()
|
w.drawBorderAround()
|
||||||
case BorderHorizontal:
|
case BorderHorizontal:
|
||||||
w.drawBorderHorizontal()
|
w.drawBorderHorizontal()
|
||||||
@@ -709,16 +672,20 @@ func (w *LightWindow) drawBorderHorizontal() {
|
|||||||
|
|
||||||
func (w *LightWindow) drawBorderAround() {
|
func (w *LightWindow) drawBorderAround() {
|
||||||
w.Move(0, 0)
|
w.Move(0, 0)
|
||||||
w.CPrint(ColPreviewBorder, AttrRegular,
|
color := ColBorder
|
||||||
|
if w.preview {
|
||||||
|
color = ColPreviewBorder
|
||||||
|
}
|
||||||
|
w.CPrint(color, AttrRegular,
|
||||||
string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.topRight))
|
string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.topRight))
|
||||||
for y := 1; y < w.height-1; y++ {
|
for y := 1; y < w.height-1; y++ {
|
||||||
w.Move(y, 0)
|
w.Move(y, 0)
|
||||||
w.CPrint(ColPreviewBorder, AttrRegular, string(w.border.vertical))
|
w.CPrint(color, AttrRegular, string(w.border.vertical))
|
||||||
w.CPrint(ColPreviewBorder, AttrRegular, repeat(' ', w.width-2))
|
w.CPrint(color, AttrRegular, repeat(' ', w.width-2))
|
||||||
w.CPrint(ColPreviewBorder, AttrRegular, string(w.border.vertical))
|
w.CPrint(color, AttrRegular, string(w.border.vertical))
|
||||||
}
|
}
|
||||||
w.Move(w.height-1, 0)
|
w.Move(w.height-1, 0)
|
||||||
w.CPrint(ColPreviewBorder, AttrRegular,
|
w.CPrint(color, AttrRegular,
|
||||||
string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight))
|
string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
110
src/tui/light_unix.go
Normal file
110
src/tui/light_unix.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/junegunn/fzf/src/util"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsLightRendererSupported() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) defaultTheme() *ColorTheme {
|
||||||
|
if strings.Contains(os.Getenv("TERM"), "256") {
|
||||||
|
return Dark256
|
||||||
|
}
|
||||||
|
colors, err := exec.Command("tput", "colors").Output()
|
||||||
|
if err == nil && atoi(strings.TrimSpace(string(colors)), 16) > 16 {
|
||||||
|
return Dark256
|
||||||
|
}
|
||||||
|
return Default16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) fd() int {
|
||||||
|
return int(r.ttyin.Fd())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) initPlatform() error {
|
||||||
|
fd := r.fd()
|
||||||
|
origState, err := terminal.GetState(fd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.origState = origState
|
||||||
|
terminal.MakeRaw(fd)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) closePlatform() {
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
|
|
||||||
|
func openTtyIn() *os.File {
|
||||||
|
in, err := os.OpenFile(consoleDevice, syscall.O_RDONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
tty := ttyname()
|
||||||
|
if len(tty) > 0 {
|
||||||
|
if in, err := os.OpenFile(tty, syscall.O_RDONLY, 0); err == nil {
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintln(os.Stderr, "Failed to open "+consoleDevice)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) setupTerminal() {
|
||||||
|
terminal.MakeRaw(r.fd())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) restoreTerminal() {
|
||||||
|
terminal.Restore(r.fd(), r.origState)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) updateTerminalSize() {
|
||||||
|
width, height, err := terminal.GetSize(r.fd())
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
r.width = width
|
||||||
|
r.height = r.maxHeightFunc(height)
|
||||||
|
} else {
|
||||||
|
r.width = getEnv("COLUMNS", defaultWidth)
|
||||||
|
r.height = r.maxHeightFunc(getEnv("LINES", defaultHeight))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) findOffset() (row int, col int) {
|
||||||
|
r.csi("6n")
|
||||||
|
r.flush()
|
||||||
|
bytes := []byte{}
|
||||||
|
for tries := 0; tries < offsetPollTries; tries++ {
|
||||||
|
bytes = r.getBytesInternal(bytes, tries > 0)
|
||||||
|
offsets := offsetRegexp.FindSubmatch(bytes)
|
||||||
|
if len(offsets) > 3 {
|
||||||
|
// Add anything we skipped over to the input buffer
|
||||||
|
r.buffer = append(r.buffer, offsets[1]...)
|
||||||
|
return atoi(string(offsets[2]), 0) - 1, atoi(string(offsets[3]), 0) - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) getch(nonblock bool) (int, bool) {
|
||||||
|
b := make([]byte, 1)
|
||||||
|
fd := r.fd()
|
||||||
|
util.SetNonblock(r.ttyin, nonblock)
|
||||||
|
_, err := util.Read(fd, b)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return int(b[0]), true
|
||||||
|
}
|
140
src/tui/light_windows.go
Normal file
140
src/tui/light_windows.go
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
//+build windows
|
||||||
|
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/junegunn/fzf/src/util"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
consoleFlagsInput = uint32(windows.ENABLE_VIRTUAL_TERMINAL_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_EXTENDED_FLAGS)
|
||||||
|
consoleFlagsOutput = uint32(windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING | windows.ENABLE_PROCESSED_OUTPUT | windows.DISABLE_NEWLINE_AUTO_RETURN)
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsLightRendererSupported checks to see if the Light renderer is supported
|
||||||
|
func IsLightRendererSupported() bool {
|
||||||
|
var oldState uint32
|
||||||
|
// enable vt100 emulation (https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences)
|
||||||
|
if windows.GetConsoleMode(windows.Stderr, &oldState) != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// attempt to set mode to determine if we support VT 100 codes. This will work on newer Windows 10
|
||||||
|
// version:
|
||||||
|
canSetVt100 := windows.SetConsoleMode(windows.Stderr, oldState|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) == nil
|
||||||
|
var checkState uint32
|
||||||
|
if windows.GetConsoleMode(windows.Stderr, &checkState) != nil ||
|
||||||
|
(checkState&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) != windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
windows.SetConsoleMode(windows.Stderr, oldState)
|
||||||
|
return canSetVt100
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) defaultTheme() *ColorTheme {
|
||||||
|
// the getenv check is borrowed from here: https://github.com/gdamore/tcell/commit/0c473b86d82f68226a142e96cc5a34c5a29b3690#diff-b008fcd5e6934bf31bc3d33bf49f47d8R178:
|
||||||
|
if !IsLightRendererSupported() || os.Getenv("ConEmuPID") != "" || os.Getenv("TCELL_TRUECOLOR") == "disable" {
|
||||||
|
return Default16
|
||||||
|
}
|
||||||
|
return Dark256
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) initPlatform() error {
|
||||||
|
//outHandle := windows.Stdout
|
||||||
|
outHandle, _ := syscall.Open("CONOUT$", syscall.O_RDWR, 0)
|
||||||
|
// enable vt100 emulation (https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences)
|
||||||
|
if err := windows.GetConsoleMode(windows.Handle(outHandle), &r.origStateOutput); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.outHandle = uintptr(outHandle)
|
||||||
|
inHandle, _ := syscall.Open("CONIN$", syscall.O_RDWR, 0)
|
||||||
|
if err := windows.GetConsoleMode(windows.Handle(inHandle), &r.origStateInput); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.inHandle = uintptr(inHandle)
|
||||||
|
|
||||||
|
r.setupTerminal()
|
||||||
|
|
||||||
|
// channel for non-blocking reads. Buffer to make sure
|
||||||
|
// we get the ESC sets:
|
||||||
|
r.ttyinChannel = make(chan byte, 12)
|
||||||
|
|
||||||
|
// the following allows for non-blocking IO.
|
||||||
|
// syscall.SetNonblock() is a NOOP under Windows.
|
||||||
|
go func() {
|
||||||
|
fd := int(r.inHandle)
|
||||||
|
b := make([]byte, 1)
|
||||||
|
for {
|
||||||
|
// HACK: if run from PSReadline, something resets ConsoleMode to remove ENABLE_VIRTUAL_TERMINAL_INPUT.
|
||||||
|
_ = windows.SetConsoleMode(windows.Handle(r.inHandle), consoleFlagsInput)
|
||||||
|
|
||||||
|
_, err := util.Read(fd, b)
|
||||||
|
if err == nil {
|
||||||
|
r.ttyinChannel <- b[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) closePlatform() {
|
||||||
|
windows.SetConsoleMode(windows.Handle(r.outHandle), r.origStateOutput)
|
||||||
|
windows.SetConsoleMode(windows.Handle(r.inHandle), r.origStateInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func openTtyIn() *os.File {
|
||||||
|
// not used
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) setupTerminal() error {
|
||||||
|
if err := windows.SetConsoleMode(windows.Handle(r.outHandle), consoleFlagsOutput); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return windows.SetConsoleMode(windows.Handle(r.inHandle), consoleFlagsInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) restoreTerminal() error {
|
||||||
|
if err := windows.SetConsoleMode(windows.Handle(r.inHandle), r.origStateInput); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return windows.SetConsoleMode(windows.Handle(r.outHandle), r.origStateOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) updateTerminalSize() {
|
||||||
|
var bufferInfo windows.ConsoleScreenBufferInfo
|
||||||
|
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(r.outHandle), &bufferInfo); err != nil {
|
||||||
|
r.width = getEnv("COLUMNS", defaultWidth)
|
||||||
|
r.height = r.maxHeightFunc(getEnv("LINES", defaultHeight))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
r.width = int(bufferInfo.Window.Right - bufferInfo.Window.Left)
|
||||||
|
r.height = r.maxHeightFunc(int(bufferInfo.Window.Bottom - bufferInfo.Window.Top))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) findOffset() (row int, col int) {
|
||||||
|
var bufferInfo windows.ConsoleScreenBufferInfo
|
||||||
|
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(r.outHandle), &bufferInfo); err != nil {
|
||||||
|
return -1, -1
|
||||||
|
}
|
||||||
|
return int(bufferInfo.CursorPosition.X), int(bufferInfo.CursorPosition.Y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) getch(nonblock bool) (int, bool) {
|
||||||
|
if nonblock {
|
||||||
|
select {
|
||||||
|
case bc := <-r.ttyinChannel:
|
||||||
|
return int(bc), true
|
||||||
|
default:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bc := <-r.ttyinChannel
|
||||||
|
return int(bc), true
|
||||||
|
}
|
||||||
|
}
|
@@ -28,6 +28,7 @@ type Attr tcell.Style
|
|||||||
|
|
||||||
type TcellWindow struct {
|
type TcellWindow struct {
|
||||||
color bool
|
color bool
|
||||||
|
preview bool
|
||||||
top int
|
top int
|
||||||
left int
|
left int
|
||||||
width int
|
width int
|
||||||
@@ -318,6 +319,8 @@ func (r *FullscreenRenderer) GetChar() Event {
|
|||||||
}
|
}
|
||||||
return Event{Right, 0, nil}
|
return Event{Right, 0, nil}
|
||||||
|
|
||||||
|
case tcell.KeyInsert:
|
||||||
|
return Event{Insert, 0, nil}
|
||||||
case tcell.KeyHome:
|
case tcell.KeyHome:
|
||||||
return Event{Home, 0, nil}
|
return Event{Home, 0, nil}
|
||||||
case tcell.KeyDelete:
|
case tcell.KeyDelete:
|
||||||
@@ -391,7 +394,7 @@ func (r *FullscreenRenderer) Pause(clear bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *FullscreenRenderer) Resume(clear bool) {
|
func (r *FullscreenRenderer) Resume(clear bool, sigcont bool) {
|
||||||
if clear {
|
if clear {
|
||||||
r.initScreen()
|
r.initScreen()
|
||||||
}
|
}
|
||||||
@@ -416,6 +419,7 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
|
|||||||
}
|
}
|
||||||
return &TcellWindow{
|
return &TcellWindow{
|
||||||
color: r.theme != nil,
|
color: r.theme != nil,
|
||||||
|
preview: preview,
|
||||||
top: top,
|
top: top,
|
||||||
left: left,
|
left: left,
|
||||||
width: width,
|
width: width,
|
||||||
@@ -589,7 +593,7 @@ func (w *TcellWindow) drawBorder() {
|
|||||||
|
|
||||||
var style tcell.Style
|
var style tcell.Style
|
||||||
if w.color {
|
if w.color {
|
||||||
if w.borderStyle.shape == BorderAround {
|
if w.preview {
|
||||||
style = ColPreviewBorder.style()
|
style = ColPreviewBorder.style()
|
||||||
} else {
|
} else {
|
||||||
style = ColBorder.style()
|
style = ColBorder.style()
|
||||||
@@ -603,7 +607,7 @@ func (w *TcellWindow) drawBorder() {
|
|||||||
_screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style)
|
_screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style)
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.borderStyle.shape == BorderAround {
|
if w.borderStyle.shape != BorderHorizontal {
|
||||||
for y := top; y < bot; y++ {
|
for y := top; y < bot; y++ {
|
||||||
_screen.SetContent(left, y, w.borderStyle.vertical, nil, style)
|
_screen.SetContent(left, y, w.borderStyle.vertical, nil, style)
|
||||||
_screen.SetContent(right-1, y, w.borderStyle.vertical, nil, style)
|
_screen.SetContent(right-1, y, w.borderStyle.vertical, nil, style)
|
||||||
|
@@ -66,6 +66,7 @@ const (
|
|||||||
Right
|
Right
|
||||||
Home
|
Home
|
||||||
End
|
End
|
||||||
|
Insert
|
||||||
|
|
||||||
SUp
|
SUp
|
||||||
SDown
|
SDown
|
||||||
@@ -86,6 +87,7 @@ const (
|
|||||||
F12
|
F12
|
||||||
|
|
||||||
Change
|
Change
|
||||||
|
BackwardEOF
|
||||||
|
|
||||||
AltSpace
|
AltSpace
|
||||||
AltSlash
|
AltSlash
|
||||||
@@ -209,7 +211,8 @@ type BorderShape int
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
BorderNone BorderShape = iota
|
BorderNone BorderShape = iota
|
||||||
BorderAround
|
BorderRounded
|
||||||
|
BorderSharp
|
||||||
BorderHorizontal
|
BorderHorizontal
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -227,6 +230,17 @@ type BorderCharacter int
|
|||||||
|
|
||||||
func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
||||||
if unicode {
|
if unicode {
|
||||||
|
if shape == BorderRounded {
|
||||||
|
return BorderStyle{
|
||||||
|
shape: shape,
|
||||||
|
horizontal: '─',
|
||||||
|
vertical: '│',
|
||||||
|
topLeft: '╭',
|
||||||
|
topRight: '╮',
|
||||||
|
bottomLeft: '╰',
|
||||||
|
bottomRight: '╯',
|
||||||
|
}
|
||||||
|
}
|
||||||
return BorderStyle{
|
return BorderStyle{
|
||||||
shape: shape,
|
shape: shape,
|
||||||
horizontal: '─',
|
horizontal: '─',
|
||||||
@@ -250,7 +264,7 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
|||||||
|
|
||||||
func MakeTransparentBorder() BorderStyle {
|
func MakeTransparentBorder() BorderStyle {
|
||||||
return BorderStyle{
|
return BorderStyle{
|
||||||
shape: BorderAround,
|
shape: BorderRounded,
|
||||||
horizontal: ' ',
|
horizontal: ' ',
|
||||||
vertical: ' ',
|
vertical: ' ',
|
||||||
topLeft: ' ',
|
topLeft: ' ',
|
||||||
@@ -262,7 +276,7 @@ func MakeTransparentBorder() BorderStyle {
|
|||||||
type Renderer interface {
|
type Renderer interface {
|
||||||
Init()
|
Init()
|
||||||
Pause(clear bool)
|
Pause(clear bool)
|
||||||
Resume(clear bool)
|
Resume(clear bool, sigcont bool)
|
||||||
Clear()
|
Clear()
|
||||||
RefreshWindows(windows []Window)
|
RefreshWindows(windows []Window)
|
||||||
Refresh()
|
Refresh()
|
||||||
|
@@ -1,19 +1,20 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# http://www.rubydoc.info/github/rest-client/rest-client/RestClient
|
# http://www.rubydoc.info/github/rest-client/rest-client/RestClient
|
||||||
require 'rest_client'
|
require 'rest_client'
|
||||||
require 'json'
|
require 'json'
|
||||||
|
|
||||||
if ARGV.length < 3
|
if ARGV.length < 3
|
||||||
puts "usage: #$0 <token> <version> <files...>"
|
puts "usage: #{$PROGRAM_NAME} <token> <version> <files...>"
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
token, version, *files = ARGV
|
token, version, *files = ARGV
|
||||||
base = "https://api.github.com/repos/junegunn/fzf-bin/releases"
|
base = 'https://api.github.com/repos/junegunn/fzf-bin/releases'
|
||||||
|
|
||||||
# List releases
|
# List releases
|
||||||
rels = JSON.parse(RestClient.get(base, :authorization => "token #{token}"))
|
rels = JSON.parse(RestClient.get(base, authorization: "token #{token}"))
|
||||||
rel = rels.find { |r| r['tag_name'] == version }
|
rel = rels.find { |r| r['tag_name'] == version }
|
||||||
unless rel
|
unless rel
|
||||||
puts "#{version} not found"
|
puts "#{version} not found"
|
||||||
@@ -21,25 +22,26 @@ unless rel
|
|||||||
end
|
end
|
||||||
|
|
||||||
# List assets
|
# List assets
|
||||||
assets = Hash[rel['assets'].map { |a| a.values_at *%w[name id] }]
|
assets = Hash[rel['assets'].map { |a| a.values_at('name', 'id') }]
|
||||||
|
|
||||||
files.select { |f| File.exists? f }.map do |file|
|
files.select { |f| File.exist?(f) }.map do |file|
|
||||||
Thread.new do
|
Thread.new do
|
||||||
name = File.basename file
|
name = File.basename(file)
|
||||||
|
|
||||||
if asset_id = assets[name]
|
if asset_id = assets[name] # rubocop:todo Lint/AssignmentInCondition
|
||||||
puts "#{name} found. Deleting asset id #{asset_id}."
|
puts "#{name} found. Deleting asset id #{asset_id}."
|
||||||
RestClient.delete "#{base}/assets/#{asset_id}",
|
RestClient.delete("#{base}/assets/#{asset_id}",
|
||||||
:authorization => "token #{token}"
|
authorization: "token #{token}")
|
||||||
else
|
else
|
||||||
puts "#{name} not found"
|
puts "#{name} not found"
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "Uploading #{name}"
|
puts "Uploading #{name}"
|
||||||
RestClient.post(
|
RestClient.post(
|
||||||
"#{base.sub 'api', 'uploads'}/#{rel['id']}/assets?name=#{name}",
|
"#{base.sub('api', 'uploads')}/#{rel['id']}/assets?name=#{name}",
|
||||||
File.read(file),
|
File.read(file),
|
||||||
:authorization => "token #{token}",
|
authorization: "token #{token}",
|
||||||
:content_type => "application/octet-stream")
|
content_type: 'application/octet-stream'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end.each(&:join)
|
end.each(&:join)
|
||||||
|
@@ -1,32 +1,34 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
func convertBoolToInt32(b bool) int32 {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// AtomicBool is a boxed-class that provides synchronized access to the
|
// AtomicBool is a boxed-class that provides synchronized access to the
|
||||||
// underlying boolean value
|
// underlying boolean value
|
||||||
type AtomicBool struct {
|
type AtomicBool struct {
|
||||||
mutex sync.Mutex
|
state int32 // "1" is true, "0" is false
|
||||||
state bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAtomicBool returns a new AtomicBool
|
// NewAtomicBool returns a new AtomicBool
|
||||||
func NewAtomicBool(initialState bool) *AtomicBool {
|
func NewAtomicBool(initialState bool) *AtomicBool {
|
||||||
return &AtomicBool{
|
return &AtomicBool{state: convertBoolToInt32(initialState)}
|
||||||
mutex: sync.Mutex{},
|
|
||||||
state: initialState}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the current boolean value synchronously
|
// Get returns the current boolean value synchronously
|
||||||
func (a *AtomicBool) Get() bool {
|
func (a *AtomicBool) Get() bool {
|
||||||
a.mutex.Lock()
|
return atomic.LoadInt32(&a.state) == 1
|
||||||
defer a.mutex.Unlock()
|
|
||||||
return a.state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set updates the boolean value synchronously
|
// Set updates the boolean value synchronously
|
||||||
func (a *AtomicBool) Set(newState bool) bool {
|
func (a *AtomicBool) Set(newState bool) bool {
|
||||||
a.mutex.Lock()
|
atomic.StoreInt32(&a.state, convertBoolToInt32(newState))
|
||||||
defer a.mutex.Unlock()
|
return newState
|
||||||
a.state = newState
|
|
||||||
return a.state
|
|
||||||
}
|
}
|
||||||
|
@@ -130,6 +130,18 @@ func (chars *Chars) TrimLength() uint16 {
|
|||||||
return chars.trimLength
|
return chars.trimLength
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (chars *Chars) LeadingWhitespaces() int {
|
||||||
|
whitespaces := 0
|
||||||
|
for i := 0; i < chars.Length(); i++ {
|
||||||
|
char := chars.Get(i)
|
||||||
|
if !unicode.IsSpace(char) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
whitespaces++
|
||||||
|
}
|
||||||
|
return whitespaces
|
||||||
|
}
|
||||||
|
|
||||||
func (chars *Chars) TrailingWhitespaces() int {
|
func (chars *Chars) TrailingWhitespaces() int {
|
||||||
whitespaces := 0
|
whitespaces := 0
|
||||||
for i := chars.Length() - 1; i >= 0; i-- {
|
for i := chars.Length() - 1; i >= 0; i-- {
|
||||||
|
1686
test/test_go.rb
1686
test/test_go.rb
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user