mirror of
https://github.com/junegunn/fzf.git
synced 2025-07-31 20:22:01 -07:00
Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d01ae55109 | ||
|
8868d7d188 | ||
|
2eec9892be | ||
|
01ae621f11 | ||
|
f984aa0d2c | ||
|
0881a6bc17 | ||
|
2c6a73546d | ||
|
a29944660e | ||
|
f6ce624c6f | ||
|
c09ec8e4d1 | ||
|
31bbaad06e | ||
|
b9ca1fe830 | ||
|
e61585f2f3 | ||
|
0de1aacb0c | ||
|
168829b555 | ||
|
170fc517d0 | ||
|
0fbf1c7c71 | ||
|
694be39c71 | ||
|
dad26d81df | ||
|
bcaea097ea | ||
|
d56fe74e24 | ||
|
4603d540c3 | ||
|
f9d53303bb | ||
|
d04faa6505 | ||
|
07da058eae | ||
|
cefa6b9878 |
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -27,18 +27,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@86f3159a697a097a813ad9bfa0002412d97690a4
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@86f3159a697a097a813ad9bfa0002412d97690a4
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@86f3159a697a097a813ad9bfa0002412d97690a4
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
14
.github/workflows/depsreview.yaml
vendored
Normal file
14
.github/workflows/depsreview.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: 'Dependency Review'
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v3
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v2
|
6
.github/workflows/linux.yml
vendored
6
.github/workflows/linux.yml
vendored
@@ -15,17 +15,17 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f # v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
|
||||
- name: Setup Ruby
|
||||
uses: ruby/setup-ruby@3068fa83f9cbd7ae106cac45483635a2f3a195c9
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 3.0.0
|
||||
|
||||
|
6
.github/workflows/macos.yml
vendored
6
.github/workflows/macos.yml
vendored
@@ -15,17 +15,17 @@ jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f # v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Setup Ruby
|
||||
uses: ruby/setup-ruby@3068fa83f9cbd7ae106cac45483635a2f3a195c9
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 3.0.0
|
||||
|
||||
|
@@ -72,6 +72,7 @@ builds:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
- loong64
|
||||
goarm:
|
||||
- 5
|
||||
- 6
|
||||
@@ -99,6 +100,10 @@ archives:
|
||||
files:
|
||||
- non-existent*
|
||||
|
||||
checksum:
|
||||
extra_files:
|
||||
- glob: ./dist/fzf-*darwin*.zip
|
||||
|
||||
release:
|
||||
github:
|
||||
owner: junegunn
|
||||
|
@@ -26,3 +26,5 @@ Style/OptionalBooleanParameter:
|
||||
Enabled: false
|
||||
Style/WordArray:
|
||||
MinSize: 1
|
||||
Minitest/AssertEqual:
|
||||
Enabled: false
|
||||
|
42
CHANGELOG.md
42
CHANGELOG.md
@@ -1,6 +1,48 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
0.35.0
|
||||
------
|
||||
- Added `start` event that is triggered only once when fzf finder starts.
|
||||
Since fzf consumes the input stream asynchronously, the input list is not
|
||||
available unless you use `--sync`.
|
||||
```sh
|
||||
seq 100 | fzf --multi --sync --bind 'start:last+select-all+preview(echo welcome)'
|
||||
```
|
||||
- Added `--border-label` and `--border-label-pos` for putting label on the border
|
||||
```sh
|
||||
# ANSI color codes are supported
|
||||
# (with https://github.com/busyloop/lolcat)
|
||||
label=$(curl -s http://metaphorpsum.com/sentences/1 | lolcat -f)
|
||||
|
||||
# Border label at the center
|
||||
fzf --height=10 --border --border-label="╢ $label ╟" --color=label:italic:black
|
||||
|
||||
# Left-aligned (positive integer)
|
||||
fzf --height=10 --border --border-label="╢ $label ╟" --border-label-pos=3 --color=label:italic:black
|
||||
|
||||
# Right-aligned (negative integer) on the bottom line (:bottom)
|
||||
fzf --height=10 --border --border-label="╢ $label ╟" --border-label-pos=-3:bottom --color=label:italic:black
|
||||
```
|
||||
- Also added `--preview-label` and `--preview-label-pos` for the border of the
|
||||
preview window
|
||||
```sh
|
||||
fzf --preview 'cat {}' --border --preview-label=' Preview ' --preview-label-pos=2
|
||||
```
|
||||
- Info panel (match counter) will be followed by a horizontal separator by
|
||||
default
|
||||
- Use `--no-separator` or `--separator=''` to hide the separator
|
||||
- You can specify an arbitrary string that is repeated to form the
|
||||
horizontal separator. e.g. `--separator=╸`
|
||||
- The color of the separator can be customized via `--color=separator:...`
|
||||
- ANSI color codes are also supported
|
||||
```sh
|
||||
fzf --separator=╸ --color=separator:green
|
||||
fzf --separator=$(lolcat -f -F 1.4 <<< ▁▁▂▃▄▅▆▆▅▄▃▂▁▁) --info=inline
|
||||
```
|
||||
- Added `--border=bold` and `--border=double` along with
|
||||
`--preview-window=border-bold` and `--preview-window=border-double`
|
||||
|
||||
0.34.0
|
||||
------
|
||||
- Added support for adaptive `--height`. If the `--height` value is prefixed
|
||||
|
33
install
33
install
@@ -2,7 +2,7 @@
|
||||
|
||||
set -u
|
||||
|
||||
version=0.34.0
|
||||
version=0.35.0
|
||||
auto_completion=
|
||||
key_bindings=
|
||||
update_config=2
|
||||
@@ -168,21 +168,22 @@ archi=$(uname -sm)
|
||||
binary_available=1
|
||||
binary_error=""
|
||||
case "$archi" in
|
||||
Darwin\ arm64) download fzf-$version-darwin_arm64.zip ;;
|
||||
Darwin\ x86_64) download fzf-$version-darwin_amd64.zip ;;
|
||||
Linux\ armv5*) download fzf-$version-linux_armv5.tar.gz ;;
|
||||
Linux\ armv6*) download fzf-$version-linux_armv6.tar.gz ;;
|
||||
Linux\ armv7*) download fzf-$version-linux_armv7.tar.gz ;;
|
||||
Linux\ armv8*) download fzf-$version-linux_arm64.tar.gz ;;
|
||||
Linux\ aarch64*) download fzf-$version-linux_arm64.tar.gz ;;
|
||||
Linux\ *64) download fzf-$version-linux_amd64.tar.gz ;;
|
||||
FreeBSD\ *64) download fzf-$version-freebsd_amd64.tar.gz ;;
|
||||
OpenBSD\ *64) download fzf-$version-openbsd_amd64.tar.gz ;;
|
||||
CYGWIN*\ *64) download fzf-$version-windows_amd64.zip ;;
|
||||
MINGW*\ *64) download fzf-$version-windows_amd64.zip ;;
|
||||
MSYS*\ *64) download fzf-$version-windows_amd64.zip ;;
|
||||
Windows*\ *64) download fzf-$version-windows_amd64.zip ;;
|
||||
*) binary_available=0 binary_error=1 ;;
|
||||
Darwin\ arm64) download fzf-$version-darwin_arm64.zip ;;
|
||||
Darwin\ x86_64) download fzf-$version-darwin_amd64.zip ;;
|
||||
Linux\ armv5*) download fzf-$version-linux_armv5.tar.gz ;;
|
||||
Linux\ armv6*) download fzf-$version-linux_armv6.tar.gz ;;
|
||||
Linux\ armv7*) download fzf-$version-linux_armv7.tar.gz ;;
|
||||
Linux\ armv8*) download fzf-$version-linux_arm64.tar.gz ;;
|
||||
Linux\ aarch64*) download fzf-$version-linux_arm64.tar.gz ;;
|
||||
Linux\ loongarch64) download fzf-$version-linux_loong64.tar.gz ;;
|
||||
Linux\ *64) download fzf-$version-linux_amd64.tar.gz ;;
|
||||
FreeBSD\ *64) download fzf-$version-freebsd_amd64.tar.gz ;;
|
||||
OpenBSD\ *64) download fzf-$version-openbsd_amd64.tar.gz ;;
|
||||
CYGWIN*\ *64) download fzf-$version-windows_amd64.zip ;;
|
||||
MINGW*\ *64) download fzf-$version-windows_amd64.zip ;;
|
||||
MSYS*\ *64) download fzf-$version-windows_amd64.zip ;;
|
||||
Windows*\ *64) download fzf-$version-windows_amd64.zip ;;
|
||||
*) binary_available=0 binary_error=1 ;;
|
||||
esac
|
||||
|
||||
cd "$fzf_base"
|
||||
|
@@ -1,4 +1,4 @@
|
||||
$version="0.34.0"
|
||||
$version="0.35.0"
|
||||
|
||||
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
|
||||
|
2
main.go
2
main.go
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/junegunn/fzf/src/protector"
|
||||
)
|
||||
|
||||
var version string = "0.34"
|
||||
var version string = "0.35"
|
||||
var revision string = "devel"
|
||||
|
||||
func main() {
|
||||
|
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
..
|
||||
.TH fzf-tmux 1 "Sep 2022" "fzf 0.34.0" "fzf-tmux - open fzf in tmux split pane"
|
||||
.TH fzf-tmux 1 "Nov 2022" "fzf 0.35.0" "fzf-tmux - open fzf in tmux split pane"
|
||||
|
||||
.SH NAME
|
||||
fzf-tmux - open fzf in tmux split pane
|
||||
|
106
man/man1/fzf.1
106
man/man1/fzf.1
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
..
|
||||
.TH fzf 1 "Sep 2022" "fzf 0.34.0" "fzf - a command-line fuzzy finder"
|
||||
.TH fzf 1 "Nov 2022" "fzf 0.35.0" "fzf - a command-line fuzzy finder"
|
||||
|
||||
.SH NAME
|
||||
fzf - a command-line fuzzy finder
|
||||
@@ -211,6 +211,10 @@ Draw border around the finder
|
||||
.br
|
||||
.BR sharp " Border with sharp corners"
|
||||
.br
|
||||
.BR bold " Border with bold lines"
|
||||
.br
|
||||
.BR double " Border with double lines"
|
||||
.br
|
||||
.BR horizontal " Horizontal lines above and below the finder"
|
||||
.br
|
||||
.BR vertical " Vertical lines on each side of the finder"
|
||||
@@ -226,6 +230,50 @@ Draw border around the finder
|
||||
.BR none
|
||||
.br
|
||||
|
||||
.TP
|
||||
.BI "--border-label" [=LABEL]
|
||||
Label to print on the horizontal border line. Should be used with one of the
|
||||
following \fB--border\fR options.
|
||||
|
||||
.br
|
||||
.B * rounded
|
||||
.br
|
||||
.B * sharp
|
||||
.br
|
||||
.B * bold
|
||||
.br
|
||||
.B * double
|
||||
.br
|
||||
.B * horizontal
|
||||
.br
|
||||
.BR "* top" " (up)"
|
||||
.br
|
||||
.BR "* bottom" " (down)"
|
||||
.br
|
||||
|
||||
.br
|
||||
e.g.
|
||||
\fB# ANSI color codes are supported
|
||||
# (with https://github.com/busyloop/lolcat)
|
||||
label=$(curl -s http://metaphorpsum.com/sentences/1 | lolcat -f)
|
||||
|
||||
# Border label at the center
|
||||
fzf --height=10 --border --border-label="╢ $label ╟" --color=label:italic:black
|
||||
|
||||
# Left-aligned (positive integer)
|
||||
fzf --height=10 --border --border-label="╢ $label ╟" --border-label-pos=3 --color=label:italic:black
|
||||
|
||||
# Right-aligned (negative integer) on the bottom line (:bottom)
|
||||
fzf --height=10 --border --border-label="╢ $label ╟" --border-label-pos=-3:bottom --color=label:italic:black\fR
|
||||
|
||||
.TP
|
||||
.BI "--border-label-pos" [=N[:top|bottom]]
|
||||
Position of the border label on the border line. Specify a positive integer as
|
||||
the column position from the left. Specify a negative integer to right-align
|
||||
the label. Label is printed on the top border line by default, add
|
||||
\fB:bottom\fR to put it on the border line on the bottom. The default value
|
||||
\fB0 (or \fBcenter\fR) will put the label at the center of the border line.
|
||||
|
||||
.TP
|
||||
.B "--no-unicode"
|
||||
Use ASCII characters instead of Unicode box drawing characters to draw border
|
||||
@@ -281,7 +329,7 @@ e.g.
|
||||
|
||||
.TP
|
||||
.BI "--info=" "STYLE"
|
||||
Determines the display style of finder info.
|
||||
Determines the display style of finder info (match counters).
|
||||
|
||||
.br
|
||||
.BR default " Display on the next line to the prompt"
|
||||
@@ -295,6 +343,18 @@ Determines the display style of finder info.
|
||||
.B "--no-info"
|
||||
A synonym for \fB--info=hidden\fB
|
||||
|
||||
.TP
|
||||
.BI "--separator=" "STR"
|
||||
The given string will be repeated to form the horizontal separator on the info
|
||||
line (default: '─' or '-' depending on \fB--no-unicode\fR).
|
||||
|
||||
ANSI color codes are supported.
|
||||
|
||||
.TP
|
||||
.B "--no-separator"
|
||||
Do not display horizontal separator on the info line. A synonym for
|
||||
\fB--separator=''\fB
|
||||
|
||||
.TP
|
||||
.BI "--prompt=" "STR"
|
||||
Input prompt (default: '> ')
|
||||
@@ -355,7 +415,9 @@ color mappings.
|
||||
\fBquery \fRQuery string
|
||||
\fBdisabled \fRQuery string when search is disabled
|
||||
\fBinfo \fRInfo line (match counters)
|
||||
\fBseparator \fRHorizontal separator on info line (match counters)
|
||||
\fBborder \fRBorder around the window (\fB--border\fR and \fB--preview\fR)
|
||||
\fBlabel \fRBorder label (\fB--border-label\fR and \fB--preview-label\fR)
|
||||
\fBprompt \fRPrompt
|
||||
\fBpointer \fRPointer to the current line
|
||||
\fBmarker \fRMulti-select marker
|
||||
@@ -486,6 +548,37 @@ e.g.
|
||||
sleep 0.01
|
||||
done'\fR
|
||||
.RE
|
||||
|
||||
.TP
|
||||
.BI "--preview-label" [=LABEL]
|
||||
Label to print on the horizontal border line of the preview window.
|
||||
Should be used with one of the following \fB--preview-window\fR options.
|
||||
|
||||
.br
|
||||
.B * border-rounded (default)
|
||||
.br
|
||||
.B * border-sharp
|
||||
.br
|
||||
.B * border-bold
|
||||
.br
|
||||
.B * border-double
|
||||
.br
|
||||
.B * border-horizontal
|
||||
.br
|
||||
.B * border-top
|
||||
.br
|
||||
.B * border-bottom
|
||||
.br
|
||||
|
||||
.TP
|
||||
.BI "--preview-label-pos" [=N[:top|bottom]]
|
||||
Position of the border label on the border line of the preview window. Specify
|
||||
a positive integer as the column position from the left. Specify a negative
|
||||
integer to right-align the label. Label is printed on the top border line by
|
||||
default, add \fB:bottom\fR to put it on the border line on the bottom. The
|
||||
default value 0 (or \fBcenter\fR) will put the label at the center of the
|
||||
border line.
|
||||
|
||||
.TP
|
||||
.BI "--preview-window=" "[POSITION][,SIZE[%]][,border-BORDER_OPT][,[no]wrap][,[no]follow][,[no]cycle][,[no]hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]"
|
||||
|
||||
@@ -811,6 +904,15 @@ e.g.
|
||||
or any single character
|
||||
|
||||
.SS AVAILABLE EVENTS:
|
||||
\fIstart\fR
|
||||
.RS
|
||||
Triggered only once when fzf finder starts. Since fzf consumes the input stream
|
||||
asynchronously, the input list is not available unless you use \fI--sync\fR.
|
||||
|
||||
e.g.
|
||||
\fB# Move cursor to the last item and select all items
|
||||
seq 1000 | fzf --multi --sync --bind start:last+select-all\fR
|
||||
.RE
|
||||
\fIchange\fR
|
||||
.RS
|
||||
Triggered whenever the query string is changed
|
||||
|
@@ -143,7 +143,7 @@ function! fzf#install()
|
||||
if !filereadable(script)
|
||||
throw script.' not found'
|
||||
endif
|
||||
let script = 'powershell -ExecutionPolicy Bypass -file ' . script
|
||||
let script = 'powershell -ExecutionPolicy Bypass -file ' . shellescape(script)
|
||||
else
|
||||
let script = s:base_dir.'/install'
|
||||
if !executable(script)
|
||||
@@ -164,7 +164,7 @@ function s:get_version(bin)
|
||||
if has_key(s:versions, a:bin)
|
||||
return s:versions[a:bin]
|
||||
end
|
||||
let command = (&shell =~ 'powershell' ? '&' : '') . shellescape(a:bin) . ' --version --no-height'
|
||||
let command = (&shell =~ 'powershell' ? '&' : '') . s:fzf_call('shellescape', a:bin) . ' --version --no-height'
|
||||
let output = systemlist(command)
|
||||
if v:shell_error || empty(output)
|
||||
return ''
|
||||
@@ -464,7 +464,7 @@ try
|
||||
let temps = { 'result': s:fzf_tempname() }
|
||||
let optstr = s:evaluate_opts(get(dict, 'options', ''))
|
||||
try
|
||||
let fzf_exec = fzf#shellescape(fzf#exec())
|
||||
let fzf_exec = shellescape(fzf#exec())
|
||||
catch
|
||||
throw v:exception
|
||||
endtry
|
||||
@@ -973,16 +973,16 @@ function! s:callback(dict, lines) abort
|
||||
endfunction
|
||||
|
||||
if has('nvim')
|
||||
function s:create_popup(hl, opts) abort
|
||||
function s:create_popup(opts) abort
|
||||
let buf = nvim_create_buf(v:false, v:true)
|
||||
let opts = extend({'relative': 'editor', 'style': 'minimal'}, a:opts)
|
||||
let win = nvim_open_win(buf, v:true, opts)
|
||||
call setwinvar(win, '&winhighlight', 'NormalFloat:'..a:hl)
|
||||
silent! call setwinvar(win, '&winhighlight', 'Pmenu:,Normal:Normal')
|
||||
call setwinvar(win, '&colorcolumn', '')
|
||||
return buf
|
||||
endfunction
|
||||
else
|
||||
function! s:create_popup(hl, opts) abort
|
||||
function! s:create_popup(opts) abort
|
||||
let s:popup_create = {buf -> popup_create(buf, #{
|
||||
\ line: a:opts.row,
|
||||
\ col: a:opts.col,
|
||||
@@ -1017,7 +1017,7 @@ function! s:popup(opts) abort
|
||||
let row += !has('nvim')
|
||||
let col += !has('nvim')
|
||||
|
||||
call s:create_popup('Normal', {
|
||||
call s:create_popup({
|
||||
\ 'row': row, 'col': col, 'width': width, 'height': height
|
||||
\ })
|
||||
endfunction
|
||||
|
@@ -37,7 +37,7 @@ bind '"\e[0n": redraw-current-line' 2> /dev/null
|
||||
__fzf_comprun() {
|
||||
if [[ "$(type -t _fzf_comprun 2>&1)" = function ]]; then
|
||||
_fzf_comprun "$@"
|
||||
elif [[ -n "$TMUX_PANE" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "$FZF_TMUX_OPTS" ]]; }; then
|
||||
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
|
||||
@@ -55,8 +55,8 @@ __fzf_orig_completion() {
|
||||
cmd="${BASH_REMATCH[3]}"
|
||||
[[ "$f" = _fzf_* ]] && continue
|
||||
printf -v "_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}" "%s" "${comp} %s ${cmd} #${f}"
|
||||
if [[ "$l" = *" -o nospace "* ]] && [[ ! "$__fzf_nospace_commands" = *" $cmd "* ]]; then
|
||||
__fzf_nospace_commands="$__fzf_nospace_commands $cmd "
|
||||
if [[ "$l" = *" -o nospace "* ]] && [[ ! "${__fzf_nospace_commands-}" = *" $cmd "* ]]; then
|
||||
__fzf_nospace_commands="${__fzf_nospace_commands-} $cmd "
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -139,17 +139,18 @@ _fzf_handle_dynamic_completion() {
|
||||
shift
|
||||
orig_cmd="$1"
|
||||
orig_var="_fzf_orig_completion_$cmd"
|
||||
orig="${!orig_var##*#}"
|
||||
orig="${!orig_var-}"
|
||||
orig="${orig##*#}"
|
||||
if [[ -n "$orig" ]] && type "$orig" > /dev/null 2>&1; then
|
||||
$orig "$@"
|
||||
elif [[ -n "$_fzf_completion_loader" ]]; then
|
||||
elif [[ -n "${_fzf_completion_loader-}" ]]; then
|
||||
orig_complete=$(complete -p "$orig_cmd" 2> /dev/null)
|
||||
_completion_loader "$@"
|
||||
ret=$?
|
||||
# _completion_loader may not have updated completion for the command
|
||||
if [[ "$(complete -p "$orig_cmd" 2> /dev/null)" != "$orig_complete" ]]; then
|
||||
__fzf_orig_completion < <(complete -p "$orig_cmd" 2> /dev/null)
|
||||
if [[ "$__fzf_nospace_commands" = *" $orig_cmd "* ]]; then
|
||||
if [[ "${__fzf_nospace_commands-}" = *" $orig_cmd "* ]]; then
|
||||
eval "${orig_complete/ -F / -o nospace -F }"
|
||||
else
|
||||
eval "$orig_complete"
|
||||
@@ -173,6 +174,7 @@ __fzf_generic_path_completion() {
|
||||
base=${cur:0:${#cur}-${#trigger}}
|
||||
eval "base=$base"
|
||||
|
||||
dir=
|
||||
[[ $base = *"/"* ]] && dir="$base"
|
||||
while true; do
|
||||
if [[ -z "$dir" ]] || [[ -d "$dir" ]]; then
|
||||
@@ -180,11 +182,11 @@ __fzf_generic_path_completion() {
|
||||
leftover=${leftover/#\/}
|
||||
[[ -z "$dir" ]] && dir='.'
|
||||
[[ "$dir" != "/" ]] && dir="${dir/%\//}"
|
||||
matches=$(eval "$1 $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $2" __fzf_comprun "$4" -q "$leftover" | while read -r item; do
|
||||
matches=$(eval "$1 $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-} $2" __fzf_comprun "$4" -q "$leftover" | while read -r item; do
|
||||
printf "%q " "${item%$3}$3"
|
||||
done)
|
||||
matches=${matches% }
|
||||
[[ -z "$3" ]] && [[ "$__fzf_nospace_commands" = *" ${COMP_WORDS[0]} "* ]] && matches="$matches "
|
||||
[[ -z "$3" ]] && [[ "${__fzf_nospace_commands-}" = *" ${COMP_WORDS[0]} "* ]] && matches="$matches "
|
||||
if [[ -n "$matches" ]]; then
|
||||
COMPREPLY=( "$matches" )
|
||||
else
|
||||
@@ -236,7 +238,7 @@ _fzf_complete() {
|
||||
if [[ "$cur" == *"$trigger" ]]; then
|
||||
cur=${cur:0:${#cur}-${#trigger}}
|
||||
|
||||
selected=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $str_arg" __fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | $post | tr '\n' ' ')
|
||||
selected=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${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"
|
||||
if [[ -n "$selected" ]]; then
|
||||
COMPREPLY=("$selected")
|
||||
@@ -329,7 +331,7 @@ __fzf_defc() {
|
||||
func="$2"
|
||||
opts="$3"
|
||||
orig_var="_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
|
||||
orig="${!orig_var}"
|
||||
orig="${!orig_var-}"
|
||||
if [[ -n "$orig" ]]; then
|
||||
printf -v def "$orig" "$func"
|
||||
eval "$def"
|
||||
|
@@ -99,9 +99,9 @@ fi
|
||||
__fzf_comprun() {
|
||||
if [[ "$(type _fzf_comprun 2>&1)" =~ function ]]; then
|
||||
_fzf_comprun "$@"
|
||||
elif [ -n "$TMUX_PANE" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "$FZF_TMUX_OPTS" ]; }; then
|
||||
elif [ -n "${TMUX_PANE-}" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "${FZF_TMUX_OPTS-}" ]; }; then
|
||||
shift
|
||||
if [ -n "$FZF_TMUX_OPTS" ]; then
|
||||
if [ -n "${FZF_TMUX_OPTS-}" ]; then
|
||||
fzf-tmux ${(Q)${(Z+n+)FZF_TMUX_OPTS}} -- "$@"
|
||||
else
|
||||
fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%} -- "$@"
|
||||
@@ -145,7 +145,7 @@ __fzf_generic_path_completion() {
|
||||
leftover=${leftover/#\/}
|
||||
[ -z "$dir" ] && dir='.'
|
||||
[ "$dir" != "/" ] && dir="${dir/%\//}"
|
||||
matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" | while read item; do
|
||||
matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-}" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" | while read item; do
|
||||
item="${item%$suffix}$suffix"
|
||||
echo -n "${(q)item} "
|
||||
done)
|
||||
@@ -184,7 +184,7 @@ _fzf_complete() {
|
||||
args=("$@")
|
||||
sep=
|
||||
for i in {0..${#args[@]}}; do
|
||||
if [[ "${args[$i]}" = -- ]]; then
|
||||
if [[ "${args[$i]-}" = -- ]]; then
|
||||
sep=$i
|
||||
break
|
||||
fi
|
||||
@@ -208,7 +208,7 @@ _fzf_complete() {
|
||||
type $post > /dev/null 2>&1 || post=cat
|
||||
|
||||
_fzf_feed_fifo "$fifo"
|
||||
matches=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $str_arg" __fzf_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
|
||||
matches=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-} $str_arg" __fzf_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
|
||||
if [ -n "$matches" ]; then
|
||||
LBUFFER="$lbuf$matches"
|
||||
fi
|
||||
@@ -279,8 +279,8 @@ fzf-completion() {
|
||||
[ -z "$trigger" -a ${LBUFFER[-1]} = ' ' ] && tokens+=("")
|
||||
|
||||
# When the trigger starts with ';', it becomes a separate token
|
||||
if [[ ${LBUFFER} = *"${tokens[-2]}${tokens[-1]}" ]]; then
|
||||
tokens[-2]="${tokens[-2]}${tokens[-1]}"
|
||||
if [[ ${LBUFFER} = *"${tokens[-2]-}${tokens[-1]}" ]]; then
|
||||
tokens[-2]="${tokens[-2]-}${tokens[-1]}"
|
||||
tokens=(${tokens[0,-2]})
|
||||
fi
|
||||
|
||||
|
@@ -19,7 +19,7 @@ __fzf_select__() {
|
||||
-o -type f -print \
|
||||
-o -type d -print \
|
||||
-o -type l -print 2> /dev/null | cut -b3-"}"
|
||||
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS -m"
|
||||
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-} -m"
|
||||
eval "$cmd" |
|
||||
FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) "$@" |
|
||||
while read -r item; do
|
||||
@@ -30,7 +30,7 @@ __fzf_select__() {
|
||||
if [[ $- =~ i ]]; then
|
||||
|
||||
__fzfcmd() {
|
||||
[[ -n "$TMUX_PANE" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "$FZF_TMUX_OPTS" ]]; } &&
|
||||
[[ -n "${TMUX_PANE-}" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "${FZF_TMUX_OPTS-}" ]]; } &&
|
||||
echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
|
||||
}
|
||||
|
||||
@@ -44,13 +44,13 @@ __fzf_cd__() {
|
||||
local cmd opts dir
|
||||
cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
||||
-o -type d -print 2> /dev/null | cut -b3-"}"
|
||||
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS +m"
|
||||
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-} +m"
|
||||
dir=$(eval "$cmd" | FZF_DEFAULT_OPTS="$opts" $(__fzfcmd)) && printf 'builtin cd -- %q' "$dir"
|
||||
}
|
||||
|
||||
__fzf_history__() {
|
||||
local output opts script
|
||||
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS +m --read0"
|
||||
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort ${FZF_CTRL_R_OPTS-} +m --read0"
|
||||
script='BEGIN { getc; $/ = "\n\t"; $HISTCOUNT = $ENV{last_hist} + 1 } s/^[ *]//; print $HISTCOUNT - $. . "\t$_" if !$seen{$_}++'
|
||||
output=$(
|
||||
builtin fc -lnr -2147483648 |
|
||||
|
@@ -46,7 +46,7 @@ __fsel() {
|
||||
-o -type l -print 2> /dev/null | cut -b3-"}"
|
||||
setopt localoptions pipefail no_aliases 2> /dev/null
|
||||
local item
|
||||
eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS" $(__fzfcmd) -m "$@" | while read item; do
|
||||
eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-}" $(__fzfcmd) -m "$@" | while read item; do
|
||||
echo -n "${(q)item} "
|
||||
done
|
||||
local ret=$?
|
||||
@@ -55,7 +55,7 @@ __fsel() {
|
||||
}
|
||||
|
||||
__fzfcmd() {
|
||||
[ -n "$TMUX_PANE" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "$FZF_TMUX_OPTS" ]; } &&
|
||||
[ -n "${TMUX_PANE-}" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "${FZF_TMUX_OPTS-}" ]; } &&
|
||||
echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ fzf-cd-widget() {
|
||||
local cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
||||
-o -type d -print 2> /dev/null | cut -b3-"}"
|
||||
setopt localoptions pipefail no_aliases 2> /dev/null
|
||||
local dir="$(eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS" $(__fzfcmd) +m)"
|
||||
local dir="$(eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-}" $(__fzfcmd) +m)"
|
||||
if [[ -z "$dir" ]]; then
|
||||
zle redisplay
|
||||
return 0
|
||||
@@ -98,7 +98,7 @@ fzf-history-widget() {
|
||||
local selected num
|
||||
setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
|
||||
selected=( $(fc -rl 1 | awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
|
||||
FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,ctrl-z:ignore $FZF_CTRL_R_OPTS --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
|
||||
FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,ctrl-z:ignore ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
|
||||
local ret=$?
|
||||
if [ -n "$selected" ]; then
|
||||
num=$selected[1]
|
||||
|
475
src/options.go
475
src/options.go
@@ -18,97 +18,107 @@ import (
|
||||
const usage = `usage: fzf [options]
|
||||
|
||||
Search
|
||||
-x, --extended Extended-search mode
|
||||
(enabled by default; +x or --no-extended to disable)
|
||||
-e, --exact Enable Exact-match
|
||||
-i Case-insensitive match (default: smart-case match)
|
||||
+i Case-sensitive match
|
||||
--scheme=SCHEME Scoring scheme [default|path|history]
|
||||
--literal Do not normalize latin script letters before matching
|
||||
-n, --nth=N[,..] Comma-separated list of field index expressions
|
||||
for limiting search scope. Each can be a non-zero
|
||||
integer or a range expression ([BEGIN]..[END]).
|
||||
--with-nth=N[,..] Transform the presentation of each line using
|
||||
field index expressions
|
||||
-d, --delimiter=STR Field delimiter regex (default: AWK-style)
|
||||
+s, --no-sort Do not sort the result
|
||||
--tac Reverse the order of the input
|
||||
--disabled Do not perform search
|
||||
--tiebreak=CRI[,..] Comma-separated list of sort criteria to apply
|
||||
when the scores are tied [length|chunk|begin|end|index]
|
||||
(default: length)
|
||||
-x, --extended Extended-search mode
|
||||
(enabled by default; +x or --no-extended to disable)
|
||||
-e, --exact Enable Exact-match
|
||||
-i Case-insensitive match (default: smart-case match)
|
||||
+i Case-sensitive match
|
||||
--scheme=SCHEME Scoring scheme [default|path|history]
|
||||
--literal Do not normalize latin script letters before matching
|
||||
-n, --nth=N[,..] Comma-separated list of field index expressions
|
||||
for limiting search scope. Each can be a non-zero
|
||||
integer or a range expression ([BEGIN]..[END]).
|
||||
--with-nth=N[,..] Transform the presentation of each line using
|
||||
field index expressions
|
||||
-d, --delimiter=STR Field delimiter regex (default: AWK-style)
|
||||
+s, --no-sort Do not sort the result
|
||||
--tac Reverse the order of the input
|
||||
--disabled Do not perform search
|
||||
--tiebreak=CRI[,..] Comma-separated list of sort criteria to apply
|
||||
when the scores are tied [length|chunk|begin|end|index]
|
||||
(default: length)
|
||||
|
||||
Interface
|
||||
-m, --multi[=MAX] Enable multi-select with tab/shift-tab
|
||||
--no-mouse Disable mouse
|
||||
--bind=KEYBINDS Custom key bindings. Refer to the man page.
|
||||
--cycle Enable cyclic scroll
|
||||
--keep-right Keep the right end of the line visible on overflow
|
||||
--scroll-off=LINES Number of screen lines to keep above or below when
|
||||
scrolling to the top or to the bottom (default: 0)
|
||||
--no-hscroll Disable horizontal scroll
|
||||
--hscroll-off=COLS Number of screen columns to keep to the right of the
|
||||
highlighted substring (default: 10)
|
||||
--filepath-word Make word-wise movements respect path separators
|
||||
--jump-labels=CHARS Label characters for jump and jump-accept
|
||||
-m, --multi[=MAX] Enable multi-select with tab/shift-tab
|
||||
--no-mouse Disable mouse
|
||||
--bind=KEYBINDS Custom key bindings. Refer to the man page.
|
||||
--cycle Enable cyclic scroll
|
||||
--keep-right Keep the right end of the line visible on overflow
|
||||
--scroll-off=LINES Number of screen lines to keep above or below when
|
||||
scrolling to the top or to the bottom (default: 0)
|
||||
--no-hscroll Disable horizontal scroll
|
||||
--hscroll-off=COLS Number of screen columns to keep to the right of the
|
||||
highlighted substring (default: 10)
|
||||
--filepath-word Make word-wise movements respect path separators
|
||||
--jump-labels=CHARS Label characters for jump and jump-accept
|
||||
|
||||
Layout
|
||||
--height=[~]HEIGHT[%] Display fzf window below the cursor with the given
|
||||
height instead of using fullscreen.
|
||||
If prefixed with '~', fzf will determine the height
|
||||
according to the input size.
|
||||
--min-height=HEIGHT Minimum height when --height is given in percent
|
||||
(default: 10)
|
||||
--layout=LAYOUT Choose layout: [default|reverse|reverse-list]
|
||||
--border[=STYLE] Draw border around the finder
|
||||
[rounded|sharp|horizontal|vertical|
|
||||
top|bottom|left|right|none] (default: rounded)
|
||||
--margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
||||
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
||||
--info=STYLE Finder info style [default|inline|hidden]
|
||||
--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-lines=N The first N lines of the input are treated as header
|
||||
--header-first Print header before the prompt line
|
||||
--ellipsis=STR Ellipsis to show when line is truncated (default: '..')
|
||||
--height=[~]HEIGHT[%] Display fzf window below the cursor with the given
|
||||
height instead of using fullscreen.
|
||||
If prefixed with '~', fzf will determine the height
|
||||
according to the input size.
|
||||
--min-height=HEIGHT Minimum height when --height is given in percent
|
||||
(default: 10)
|
||||
--layout=LAYOUT Choose layout: [default|reverse|reverse-list]
|
||||
--border[=STYLE] Draw border around the finder
|
||||
[rounded|sharp|horizontal|vertical|
|
||||
top|bottom|left|right|none] (default: rounded)
|
||||
--border-label=LABEL Label to print on the border
|
||||
--border-label-pos=COL Position of the border label
|
||||
[POSITIVE_INTEGER: columns from left|
|
||||
NEGATIVE_INTEGER: columns from right][:bottom]
|
||||
(default: 0 or center)
|
||||
--margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
||||
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
||||
--info=STYLE Finder info style [default|inline|hidden]
|
||||
--separator=STR String to form horizontal separator on info line
|
||||
--no-separator Hide info line separator
|
||||
--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-lines=N The first N lines of the input are treated as header
|
||||
--header-first Print header before the prompt line
|
||||
--ellipsis=STR Ellipsis to show when line is truncated (default: '..')
|
||||
|
||||
Display
|
||||
--ansi Enable processing of ANSI color codes
|
||||
--tabstop=SPACES Number of spaces for a tab character (default: 8)
|
||||
--color=COLSPEC Base scheme (dark|light|16|bw) and/or custom colors
|
||||
--no-bold Do not use bold text
|
||||
--ansi Enable processing of ANSI color codes
|
||||
--tabstop=SPACES Number of spaces for a tab character (default: 8)
|
||||
--color=COLSPEC Base scheme (dark|light|16|bw) and/or custom colors
|
||||
--no-bold Do not use bold text
|
||||
|
||||
History
|
||||
--history=FILE History file
|
||||
--history-size=N Maximum number of history entries (default: 1000)
|
||||
--history=FILE History file
|
||||
--history-size=N Maximum number of history entries (default: 1000)
|
||||
|
||||
Preview
|
||||
--preview=COMMAND Command to preview highlighted line ({})
|
||||
--preview-window=OPT Preview window layout (default: right:50%)
|
||||
[up|down|left|right][,SIZE[%]]
|
||||
[,[no]wrap][,[no]cycle][,[no]follow][,[no]hidden]
|
||||
[,border-BORDER_OPT]
|
||||
[,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES]
|
||||
[,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]
|
||||
--preview=COMMAND Command to preview highlighted line ({})
|
||||
--preview-window=OPT Preview window layout (default: right:50%)
|
||||
[up|down|left|right][,SIZE[%]]
|
||||
[,[no]wrap][,[no]cycle][,[no]follow][,[no]hidden]
|
||||
[,border-BORDER_OPT]
|
||||
[,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES]
|
||||
[,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]
|
||||
--preview-label=LABEL
|
||||
--preview-label-pos=N Same as --border-label and --border-label-pos,
|
||||
but for preview window
|
||||
|
||||
Scripting
|
||||
-q, --query=STR Start the finder with the given query
|
||||
-1, --select-1 Automatically select the only match
|
||||
-0, --exit-0 Exit immediately when there's no match
|
||||
-f, --filter=STR Filter mode. Do not start interactive finder.
|
||||
--print-query Print query as the first line
|
||||
--expect=KEYS Comma-separated list of keys to complete fzf
|
||||
--read0 Read input delimited by ASCII NUL characters
|
||||
--print0 Print output delimited by ASCII NUL characters
|
||||
--sync Synchronous search for multi-staged filtering
|
||||
--version Display version information and exit
|
||||
-q, --query=STR Start the finder with the given query
|
||||
-1, --select-1 Automatically select the only match
|
||||
-0, --exit-0 Exit immediately when there's no match
|
||||
-f, --filter=STR Filter mode. Do not start interactive finder.
|
||||
--print-query Print query as the first line
|
||||
--expect=KEYS Comma-separated list of keys to complete fzf
|
||||
--read0 Read input delimited by ASCII NUL characters
|
||||
--print0 Print output delimited by ASCII NUL characters
|
||||
--sync Synchronous search for multi-staged filtering
|
||||
--version Display version information and exit
|
||||
|
||||
Environment variables
|
||||
FZF_DEFAULT_COMMAND Default command to use when input is tty
|
||||
FZF_DEFAULT_OPTS Default options
|
||||
(e.g. '--layout=reverse --inline-info')
|
||||
FZF_DEFAULT_COMMAND Default command to use when input is tty
|
||||
FZF_DEFAULT_OPTS Default options
|
||||
(e.g. '--layout=reverse --inline-info')
|
||||
|
||||
`
|
||||
|
||||
@@ -173,6 +183,12 @@ const (
|
||||
infoHidden
|
||||
)
|
||||
|
||||
type labelOpts struct {
|
||||
label string
|
||||
column int
|
||||
bottom bool
|
||||
}
|
||||
|
||||
type previewOpts struct {
|
||||
command string
|
||||
position windowPosition
|
||||
@@ -188,6 +204,23 @@ type previewOpts struct {
|
||||
alternative *previewOpts
|
||||
}
|
||||
|
||||
func parseLabelPosition(opts *labelOpts, arg string) {
|
||||
opts.column = 0
|
||||
opts.bottom = false
|
||||
for _, token := range splitRegexp.Split(strings.ToLower(arg), -1) {
|
||||
switch token {
|
||||
case "center":
|
||||
opts.column = 0
|
||||
case "bottom":
|
||||
opts.bottom = true
|
||||
case "top":
|
||||
opts.bottom = false
|
||||
default:
|
||||
opts.column = atoi(token)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a previewOpts) aboveOrBelow() bool {
|
||||
return a.size.size > 0 && (a.position == posUp || a.position == posDown)
|
||||
}
|
||||
@@ -204,64 +237,67 @@ func (a previewOpts) sameContentLayout(b previewOpts) bool {
|
||||
|
||||
// Options stores the values of command-line options
|
||||
type Options struct {
|
||||
Fuzzy bool
|
||||
FuzzyAlgo algo.Algo
|
||||
Scheme string
|
||||
Extended bool
|
||||
Phony bool
|
||||
Case Case
|
||||
Normalize bool
|
||||
Nth []Range
|
||||
WithNth []Range
|
||||
Delimiter Delimiter
|
||||
Sort int
|
||||
Tac bool
|
||||
Criteria []criterion
|
||||
Multi int
|
||||
Ansi bool
|
||||
Mouse bool
|
||||
Theme *tui.ColorTheme
|
||||
Black bool
|
||||
Bold bool
|
||||
Height heightSpec
|
||||
MinHeight int
|
||||
Layout layoutType
|
||||
Cycle bool
|
||||
KeepRight bool
|
||||
Hscroll bool
|
||||
HscrollOff int
|
||||
ScrollOff int
|
||||
FileWord bool
|
||||
InfoStyle infoStyle
|
||||
JumpLabels string
|
||||
Prompt string
|
||||
Pointer string
|
||||
Marker string
|
||||
Query string
|
||||
Select1 bool
|
||||
Exit0 bool
|
||||
Filter *string
|
||||
ToggleSort bool
|
||||
Expect map[tui.Event]string
|
||||
Keymap map[tui.Event][]*action
|
||||
Preview previewOpts
|
||||
PrintQuery bool
|
||||
ReadZero bool
|
||||
Printer func(string)
|
||||
PrintSep string
|
||||
Sync bool
|
||||
History *History
|
||||
Header []string
|
||||
HeaderLines int
|
||||
HeaderFirst bool
|
||||
Ellipsis string
|
||||
Margin [4]sizeSpec
|
||||
Padding [4]sizeSpec
|
||||
BorderShape tui.BorderShape
|
||||
Unicode bool
|
||||
Tabstop int
|
||||
ClearOnExit bool
|
||||
Version bool
|
||||
Fuzzy bool
|
||||
FuzzyAlgo algo.Algo
|
||||
Scheme string
|
||||
Extended bool
|
||||
Phony bool
|
||||
Case Case
|
||||
Normalize bool
|
||||
Nth []Range
|
||||
WithNth []Range
|
||||
Delimiter Delimiter
|
||||
Sort int
|
||||
Tac bool
|
||||
Criteria []criterion
|
||||
Multi int
|
||||
Ansi bool
|
||||
Mouse bool
|
||||
Theme *tui.ColorTheme
|
||||
Black bool
|
||||
Bold bool
|
||||
Height heightSpec
|
||||
MinHeight int
|
||||
Layout layoutType
|
||||
Cycle bool
|
||||
KeepRight bool
|
||||
Hscroll bool
|
||||
HscrollOff int
|
||||
ScrollOff int
|
||||
FileWord bool
|
||||
InfoStyle infoStyle
|
||||
Separator *string
|
||||
JumpLabels string
|
||||
Prompt string
|
||||
Pointer string
|
||||
Marker string
|
||||
Query string
|
||||
Select1 bool
|
||||
Exit0 bool
|
||||
Filter *string
|
||||
ToggleSort bool
|
||||
Expect map[tui.Event]string
|
||||
Keymap map[tui.Event][]*action
|
||||
Preview previewOpts
|
||||
PrintQuery bool
|
||||
ReadZero bool
|
||||
Printer func(string)
|
||||
PrintSep string
|
||||
Sync bool
|
||||
History *History
|
||||
Header []string
|
||||
HeaderLines int
|
||||
HeaderFirst bool
|
||||
Ellipsis string
|
||||
Margin [4]sizeSpec
|
||||
Padding [4]sizeSpec
|
||||
BorderShape tui.BorderShape
|
||||
BorderLabel labelOpts
|
||||
PreviewLabel labelOpts
|
||||
Unicode bool
|
||||
Tabstop int
|
||||
ClearOnExit bool
|
||||
Version bool
|
||||
}
|
||||
|
||||
func defaultPreviewOpts(command string) previewOpts {
|
||||
@@ -270,62 +306,65 @@ func defaultPreviewOpts(command string) previewOpts {
|
||||
|
||||
func defaultOptions() *Options {
|
||||
return &Options{
|
||||
Fuzzy: true,
|
||||
FuzzyAlgo: algo.FuzzyMatchV2,
|
||||
Scheme: "default",
|
||||
Extended: true,
|
||||
Phony: false,
|
||||
Case: CaseSmart,
|
||||
Normalize: true,
|
||||
Nth: make([]Range, 0),
|
||||
WithNth: make([]Range, 0),
|
||||
Delimiter: Delimiter{},
|
||||
Sort: 1000,
|
||||
Tac: false,
|
||||
Criteria: []criterion{byScore, byLength},
|
||||
Multi: 0,
|
||||
Ansi: false,
|
||||
Mouse: true,
|
||||
Theme: tui.EmptyTheme(),
|
||||
Black: false,
|
||||
Bold: true,
|
||||
MinHeight: 10,
|
||||
Layout: layoutDefault,
|
||||
Cycle: false,
|
||||
KeepRight: false,
|
||||
Hscroll: true,
|
||||
HscrollOff: 10,
|
||||
ScrollOff: 0,
|
||||
FileWord: false,
|
||||
InfoStyle: infoDefault,
|
||||
JumpLabels: defaultJumpLabels,
|
||||
Prompt: "> ",
|
||||
Pointer: ">",
|
||||
Marker: ">",
|
||||
Query: "",
|
||||
Select1: false,
|
||||
Exit0: false,
|
||||
Filter: nil,
|
||||
ToggleSort: false,
|
||||
Expect: make(map[tui.Event]string),
|
||||
Keymap: make(map[tui.Event][]*action),
|
||||
Preview: defaultPreviewOpts(""),
|
||||
PrintQuery: false,
|
||||
ReadZero: false,
|
||||
Printer: func(str string) { fmt.Println(str) },
|
||||
PrintSep: "\n",
|
||||
Sync: false,
|
||||
History: nil,
|
||||
Header: make([]string, 0),
|
||||
HeaderLines: 0,
|
||||
HeaderFirst: false,
|
||||
Ellipsis: "..",
|
||||
Margin: defaultMargin(),
|
||||
Padding: defaultMargin(),
|
||||
Unicode: true,
|
||||
Tabstop: 8,
|
||||
ClearOnExit: true,
|
||||
Version: false}
|
||||
Fuzzy: true,
|
||||
FuzzyAlgo: algo.FuzzyMatchV2,
|
||||
Scheme: "default",
|
||||
Extended: true,
|
||||
Phony: false,
|
||||
Case: CaseSmart,
|
||||
Normalize: true,
|
||||
Nth: make([]Range, 0),
|
||||
WithNth: make([]Range, 0),
|
||||
Delimiter: Delimiter{},
|
||||
Sort: 1000,
|
||||
Tac: false,
|
||||
Criteria: []criterion{byScore, byLength},
|
||||
Multi: 0,
|
||||
Ansi: false,
|
||||
Mouse: true,
|
||||
Theme: tui.EmptyTheme(),
|
||||
Black: false,
|
||||
Bold: true,
|
||||
MinHeight: 10,
|
||||
Layout: layoutDefault,
|
||||
Cycle: false,
|
||||
KeepRight: false,
|
||||
Hscroll: true,
|
||||
HscrollOff: 10,
|
||||
ScrollOff: 0,
|
||||
FileWord: false,
|
||||
InfoStyle: infoDefault,
|
||||
Separator: nil,
|
||||
JumpLabels: defaultJumpLabels,
|
||||
Prompt: "> ",
|
||||
Pointer: ">",
|
||||
Marker: ">",
|
||||
Query: "",
|
||||
Select1: false,
|
||||
Exit0: false,
|
||||
Filter: nil,
|
||||
ToggleSort: false,
|
||||
Expect: make(map[tui.Event]string),
|
||||
Keymap: make(map[tui.Event][]*action),
|
||||
Preview: defaultPreviewOpts(""),
|
||||
PrintQuery: false,
|
||||
ReadZero: false,
|
||||
Printer: func(str string) { fmt.Println(str) },
|
||||
PrintSep: "\n",
|
||||
Sync: false,
|
||||
History: nil,
|
||||
Header: make([]string, 0),
|
||||
HeaderLines: 0,
|
||||
HeaderFirst: false,
|
||||
Ellipsis: "..",
|
||||
Margin: defaultMargin(),
|
||||
Padding: defaultMargin(),
|
||||
Unicode: true,
|
||||
Tabstop: 8,
|
||||
BorderLabel: labelOpts{},
|
||||
PreviewLabel: labelOpts{},
|
||||
ClearOnExit: true,
|
||||
Version: false}
|
||||
}
|
||||
|
||||
func help(code int) {
|
||||
@@ -470,6 +509,10 @@ func parseBorder(str string, optional bool) tui.BorderShape {
|
||||
return tui.BorderRounded
|
||||
case "sharp":
|
||||
return tui.BorderSharp
|
||||
case "bold":
|
||||
return tui.BorderBold
|
||||
case "double":
|
||||
return tui.BorderDouble
|
||||
case "horizontal":
|
||||
return tui.BorderHorizontal
|
||||
case "vertical":
|
||||
@@ -488,7 +531,7 @@ func parseBorder(str string, optional bool) tui.BorderShape {
|
||||
if optional && str == "" {
|
||||
return tui.BorderRounded
|
||||
}
|
||||
errorExit("invalid border style (expected: rounded|sharp|horizontal|vertical|top|bottom|left|right|none)")
|
||||
errorExit("invalid border style (expected: rounded|sharp|bold|double|horizontal|vertical|top|bottom|left|right|none)")
|
||||
}
|
||||
return tui.BorderNone
|
||||
}
|
||||
@@ -543,6 +586,8 @@ func parseKeyChords(str string, message string) map[tui.Event]string {
|
||||
add(tui.Change)
|
||||
case "backward-eof":
|
||||
add(tui.BackwardEOF)
|
||||
case "start":
|
||||
add(tui.Start)
|
||||
case "alt-enter", "alt-return":
|
||||
chords[tui.CtrlAltKey('m')] = key
|
||||
case "alt-space":
|
||||
@@ -796,6 +841,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
||||
mergeAttr(&theme.CurrentMatch)
|
||||
case "border":
|
||||
mergeAttr(&theme.Border)
|
||||
case "separator":
|
||||
mergeAttr(&theme.Separator)
|
||||
case "label":
|
||||
mergeAttr(&theme.BorderLabel)
|
||||
case "prompt":
|
||||
mergeAttr(&theme.Prompt)
|
||||
case "spinner":
|
||||
@@ -816,7 +865,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
||||
return theme
|
||||
}
|
||||
|
||||
var executeRegexp *regexp.Regexp
|
||||
var (
|
||||
executeRegexp *regexp.Regexp
|
||||
splitRegexp *regexp.Regexp
|
||||
)
|
||||
|
||||
func firstKey(keymap map[tui.Event]string) tui.Event {
|
||||
for k := range keymap {
|
||||
@@ -836,6 +888,7 @@ func init() {
|
||||
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
|
||||
executeRegexp = regexp.MustCompile(
|
||||
`(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|(?:re|un)bind):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|(?:re|un)bind)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
|
||||
splitRegexp = regexp.MustCompile("[,:]+")
|
||||
}
|
||||
|
||||
func parseKeymap(keymap map[tui.Event][]*action, str string) {
|
||||
@@ -1203,7 +1256,7 @@ func parseInfoStyle(str string) infoStyle {
|
||||
case "hidden":
|
||||
return infoHidden
|
||||
default:
|
||||
errorExit("invalid info style (expected: default / inline / hidden)")
|
||||
errorExit("invalid info style (expected: default|inline|hidden)")
|
||||
}
|
||||
return infoDefault
|
||||
}
|
||||
@@ -1250,6 +1303,10 @@ func parsePreviewWindow(opts *previewOpts, input string) {
|
||||
opts.border = tui.BorderRounded
|
||||
case "sharp", "border-sharp":
|
||||
opts.border = tui.BorderSharp
|
||||
case "border-bold":
|
||||
opts.border = tui.BorderBold
|
||||
case "border-double":
|
||||
opts.border = tui.BorderDouble
|
||||
case "noborder", "border-none":
|
||||
opts.border = tui.BorderNone
|
||||
case "border-horizontal":
|
||||
@@ -1472,6 +1529,12 @@ func parseOptions(opts *Options, allArgs []string) {
|
||||
opts.InfoStyle = infoInline
|
||||
case "--no-inline-info":
|
||||
opts.InfoStyle = infoDefault
|
||||
case "--separator":
|
||||
separator := nextString(allArgs, &i, "separator character required")
|
||||
opts.Separator = &separator
|
||||
case "--no-separator":
|
||||
nosep := ""
|
||||
opts.Separator = &nosep
|
||||
case "--jump-labels":
|
||||
opts.JumpLabels = nextString(allArgs, &i, "label characters required")
|
||||
validateJumpLabels = true
|
||||
@@ -1554,6 +1617,20 @@ func parseOptions(opts *Options, allArgs []string) {
|
||||
case "--border":
|
||||
hasArg, arg := optionalNextString(allArgs, &i)
|
||||
opts.BorderShape = parseBorder(arg, !hasArg)
|
||||
case "--no-border-label":
|
||||
opts.BorderLabel.label = ""
|
||||
case "--border-label":
|
||||
opts.BorderLabel.label = nextString(allArgs, &i, "label required")
|
||||
case "--border-label-pos":
|
||||
pos := nextString(allArgs, &i, "label position required (positive or negative integer or 'center')")
|
||||
parseLabelPosition(&opts.BorderLabel, pos)
|
||||
case "--no-preview-label":
|
||||
opts.PreviewLabel.label = ""
|
||||
case "--preview-label":
|
||||
opts.PreviewLabel.label = nextString(allArgs, &i, "preview label required")
|
||||
case "--preview-label-pos":
|
||||
pos := nextString(allArgs, &i, "preview label position required (positive or negative integer or 'center')")
|
||||
parseLabelPosition(&opts.PreviewLabel, pos)
|
||||
case "--no-unicode":
|
||||
opts.Unicode = false
|
||||
case "--unicode":
|
||||
@@ -1589,6 +1666,14 @@ func parseOptions(opts *Options, allArgs []string) {
|
||||
opts.Delimiter = delimiterRegexp(value)
|
||||
} else if match, value := optString(arg, "--border="); match {
|
||||
opts.BorderShape = parseBorder(value, false)
|
||||
} else if match, value := optString(arg, "--border-label="); match {
|
||||
opts.BorderLabel.label = value
|
||||
} else if match, value := optString(arg, "--border-label-pos="); match {
|
||||
parseLabelPosition(&opts.BorderLabel, value)
|
||||
} else if match, value := optString(arg, "--preview-label="); match {
|
||||
opts.PreviewLabel.label = value
|
||||
} else if match, value := optString(arg, "--preview-label-pos="); match {
|
||||
parseLabelPosition(&opts.PreviewLabel, value)
|
||||
} else if match, value := optString(arg, "--prompt="); match {
|
||||
opts.Prompt = value
|
||||
} else if match, value := optString(arg, "--pointer="); match {
|
||||
@@ -1613,6 +1698,8 @@ func parseOptions(opts *Options, allArgs []string) {
|
||||
opts.Layout = parseLayout(value)
|
||||
} else if match, value := optString(arg, "--info="); match {
|
||||
opts.InfoStyle = parseInfoStyle(value)
|
||||
} else if match, value := optString(arg, "--separator="); match {
|
||||
opts.Separator = &value
|
||||
} else if match, value := optString(arg, "--toggle-sort="); match {
|
||||
parseToggleSort(opts.Keymap, value)
|
||||
} else if match, value := optString(arg, "--expect="); match {
|
||||
|
152
src/terminal.go
152
src/terminal.go
@@ -107,13 +107,23 @@ type fitpad struct {
|
||||
|
||||
var emptyLine = itemLine{}
|
||||
|
||||
type labelPrinter func(tui.Window, int)
|
||||
|
||||
// Terminal represents terminal input/output
|
||||
type Terminal struct {
|
||||
initDelay time.Duration
|
||||
infoStyle infoStyle
|
||||
separator labelPrinter
|
||||
separatorLen int
|
||||
spinner []string
|
||||
prompt func()
|
||||
promptLen int
|
||||
borderLabel labelPrinter
|
||||
borderLabelLen int
|
||||
borderLabelOpts labelOpts
|
||||
previewLabel labelPrinter
|
||||
previewLabelLen int
|
||||
previewLabelOpts labelOpts
|
||||
pointer string
|
||||
pointerLen int
|
||||
pointerEmpty string
|
||||
@@ -514,6 +524,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
t := Terminal{
|
||||
initDelay: delay,
|
||||
infoStyle: opts.InfoStyle,
|
||||
separator: nil,
|
||||
spinner: makeSpinner(opts.Unicode),
|
||||
queryLen: [2]int{0, 0},
|
||||
layout: opts.Layout,
|
||||
@@ -544,6 +555,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
padding: opts.Padding,
|
||||
unicode: opts.Unicode,
|
||||
borderShape: opts.BorderShape,
|
||||
borderLabel: nil,
|
||||
borderLabelOpts: opts.BorderLabel,
|
||||
previewLabel: nil,
|
||||
previewLabelOpts: opts.PreviewLabel,
|
||||
cleanExit: opts.ClearOnExit,
|
||||
paused: opts.Phony,
|
||||
strong: strongAttr,
|
||||
@@ -587,13 +602,24 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
// Pre-calculated empty pointer and marker signs
|
||||
t.pointerEmpty = strings.Repeat(" ", t.pointerLen)
|
||||
t.markerEmpty = strings.Repeat(" ", t.markerLen)
|
||||
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(opts.BorderLabel.label, &tui.ColBorderLabel, false)
|
||||
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(opts.PreviewLabel.label, &tui.ColBorderLabel, false)
|
||||
if opts.Separator == nil || len(*opts.Separator) > 0 {
|
||||
bar := "─"
|
||||
if opts.Separator != nil {
|
||||
bar = *opts.Separator
|
||||
} else if !t.unicode {
|
||||
bar = "-"
|
||||
}
|
||||
t.separator, t.separatorLen = t.ansiLabelPrinter(bar, &tui.ColSeparator, true)
|
||||
}
|
||||
|
||||
return &t
|
||||
}
|
||||
|
||||
func borderLines(shape tui.BorderShape) int {
|
||||
switch shape {
|
||||
case tui.BorderHorizontal, tui.BorderRounded, tui.BorderSharp:
|
||||
case tui.BorderHorizontal, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
|
||||
return 2
|
||||
case tui.BorderTop, tui.BorderBottom:
|
||||
return 1
|
||||
@@ -617,6 +643,65 @@ func (t *Terminal) MaxFitAndPad(opts *Options) (int, int) {
|
||||
return fit, padHeight
|
||||
}
|
||||
|
||||
func (t *Terminal) ansiLabelPrinter(str string, color *tui.ColorPair, fill bool) (labelPrinter, int) {
|
||||
// Nothing to do
|
||||
if len(str) == 0 {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
// Extract ANSI color codes
|
||||
text, colors, _ := extractColor(str, nil, nil)
|
||||
runes := []rune(text)
|
||||
|
||||
// Simpler printer for strings without ANSI colors or tab characters
|
||||
if colors == nil && strings.IndexRune(str, '\t') < 0 {
|
||||
length := runewidth.StringWidth(str)
|
||||
if length == 0 {
|
||||
return nil, 0
|
||||
}
|
||||
printFn := func(window tui.Window, limit int) {
|
||||
if length > limit {
|
||||
trimmedRunes, _ := t.trimRight(runes, limit)
|
||||
window.CPrint(*color, string(trimmedRunes))
|
||||
} else if fill {
|
||||
window.CPrint(*color, util.RepeatToFill(str, length, limit))
|
||||
} else {
|
||||
window.CPrint(*color, str)
|
||||
}
|
||||
}
|
||||
return printFn, len(text)
|
||||
}
|
||||
|
||||
// Printer that correctly handles ANSI color codes and tab characters
|
||||
item := &Item{text: util.RunesToChars(runes), colors: colors}
|
||||
length := t.displayWidth(runes)
|
||||
if length == 0 {
|
||||
return nil, 0
|
||||
}
|
||||
result := Result{item: item}
|
||||
var offsets []colorOffset
|
||||
printFn := func(window tui.Window, limit int) {
|
||||
if offsets == nil {
|
||||
// tui.Col* are not initialized until renderer.Init()
|
||||
offsets = result.colorOffsets(nil, t.theme, *color, *color, false)
|
||||
}
|
||||
for limit > 0 {
|
||||
if length > limit {
|
||||
trimmedRunes, _ := t.trimRight(runes, limit)
|
||||
t.printColoredString(window, trimmedRunes, offsets, *color)
|
||||
break
|
||||
} else if fill {
|
||||
t.printColoredString(window, runes, offsets, *color)
|
||||
limit -= length
|
||||
} else {
|
||||
t.printColoredString(window, runes, offsets, *color)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return printFn, length
|
||||
}
|
||||
|
||||
func (t *Terminal) parsePrompt(prompt string) (func(), int) {
|
||||
var state *ansiState
|
||||
trimmed, colors, _ := extractColor(prompt, state, nil)
|
||||
@@ -813,7 +898,7 @@ func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
|
||||
if idx == 3 {
|
||||
extraMargin[idx] += 2
|
||||
}
|
||||
case tui.BorderRounded, tui.BorderSharp:
|
||||
case tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
|
||||
extraMargin[idx] += 1 + idx%2
|
||||
}
|
||||
marginInt[idx] = sizeSpecToInt(idx, sizeSpec) + extraMargin[idx]
|
||||
@@ -905,7 +990,7 @@ func (t *Terminal) resizeWindows() {
|
||||
t.border = t.tui.NewWindow(
|
||||
marginInt[0], marginInt[3], width+2, height,
|
||||
false, tui.MakeBorderStyle(tui.BorderRight, t.unicode))
|
||||
case tui.BorderRounded, tui.BorderSharp:
|
||||
case tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
|
||||
t.border = t.tui.NewWindow(
|
||||
marginInt[0]-1, marginInt[3]-2, width+4, height+2,
|
||||
false, tui.MakeBorderStyle(t.borderShape, t.unicode))
|
||||
@@ -935,7 +1020,7 @@ func (t *Terminal) resizeWindows() {
|
||||
}
|
||||
t.pborder = t.tui.NewWindow(y, x, w, h, true, previewBorder)
|
||||
switch previewOpts.border {
|
||||
case tui.BorderSharp, tui.BorderRounded:
|
||||
case tui.BorderSharp, tui.BorderRounded, tui.BorderBold, tui.BorderDouble:
|
||||
pwidth -= 4
|
||||
pheight -= 2
|
||||
x += 2
|
||||
@@ -1015,6 +1100,34 @@ func (t *Terminal) resizeWindows() {
|
||||
width,
|
||||
height, false, noBorder)
|
||||
}
|
||||
|
||||
// Print border label
|
||||
printLabel := func(window tui.Window, render labelPrinter, opts labelOpts, length int, borderShape tui.BorderShape) {
|
||||
if window == nil || render == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch borderShape {
|
||||
case tui.BorderHorizontal, tui.BorderTop, tui.BorderBottom, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
|
||||
var col int
|
||||
if opts.column == 0 {
|
||||
col = util.Max(0, (window.Width()-length)/2)
|
||||
} else if opts.column < 0 {
|
||||
col = util.Max(0, window.Width()+opts.column+1-length)
|
||||
} else {
|
||||
col = util.Min(opts.column-1, window.Width()-length)
|
||||
}
|
||||
row := 0
|
||||
if borderShape == tui.BorderBottom || opts.bottom {
|
||||
row = window.Height() - 1
|
||||
}
|
||||
window.Move(row, col)
|
||||
render(window, window.Width())
|
||||
}
|
||||
}
|
||||
printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape)
|
||||
printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.previewOpts.border)
|
||||
|
||||
for i := 0; i < t.window.Height(); i++ {
|
||||
t.window.MoveAndClear(i, 0)
|
||||
}
|
||||
@@ -1154,8 +1267,15 @@ func (t *Terminal) printInfo() {
|
||||
if t.failed != nil && t.count == 0 {
|
||||
output = fmt.Sprintf("[Command failed: %s]", *t.failed)
|
||||
}
|
||||
output = t.trimMessage(output, t.window.Width()-pos)
|
||||
maxWidth := t.window.Width() - pos
|
||||
output = t.trimMessage(output, maxWidth)
|
||||
t.window.CPrint(tui.ColInfo, output)
|
||||
|
||||
fillLength := maxWidth - len(output) - 2
|
||||
if t.separatorLen > 0 && fillLength > 0 {
|
||||
t.window.CPrint(tui.ColSeparator, " ")
|
||||
t.separator(t.window, fillLength)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Terminal) printHeader() {
|
||||
@@ -1394,6 +1514,11 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
|
||||
displayWidth = t.displayWidthWithLimit(text, 0, displayWidth)
|
||||
}
|
||||
|
||||
t.printColoredString(t.window, text, offsets, colBase)
|
||||
return displayWidth
|
||||
}
|
||||
|
||||
func (t *Terminal) printColoredString(window tui.Window, text []rune, offsets []colorOffset, colBase tui.ColorPair) {
|
||||
var index int32
|
||||
var substr string
|
||||
var prefixWidth int
|
||||
@@ -1403,11 +1528,11 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
|
||||
e := util.Constrain32(offset.offset[1], index, maxOffset)
|
||||
|
||||
substr, prefixWidth = t.processTabs(text[index:b], prefixWidth)
|
||||
t.window.CPrint(colBase, substr)
|
||||
window.CPrint(colBase, substr)
|
||||
|
||||
if b < e {
|
||||
substr, prefixWidth = t.processTabs(text[b:e], prefixWidth)
|
||||
t.window.CPrint(offset.color, substr)
|
||||
window.CPrint(offset.color, substr)
|
||||
}
|
||||
|
||||
index = e
|
||||
@@ -1417,9 +1542,8 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
|
||||
}
|
||||
if index < maxOffset {
|
||||
substr, _ = t.processTabs(text[index:], prefixWidth)
|
||||
t.window.CPrint(colBase, substr)
|
||||
window.CPrint(colBase, substr)
|
||||
}
|
||||
return displayWidth
|
||||
}
|
||||
|
||||
func (t *Terminal) renderPreviewSpinner() {
|
||||
@@ -2362,13 +2486,21 @@ func (t *Terminal) Loop() {
|
||||
}()
|
||||
|
||||
looping := true
|
||||
_, startEvent := t.keymap[tui.Start.AsEvent()]
|
||||
|
||||
for looping {
|
||||
var newCommand *string
|
||||
changed := false
|
||||
beof := false
|
||||
queryChanged := false
|
||||
|
||||
event := t.tui.GetChar()
|
||||
var event tui.Event
|
||||
if startEvent {
|
||||
event = tui.Start.AsEvent()
|
||||
startEvent = false
|
||||
} else {
|
||||
event = t.tui.GetChar()
|
||||
}
|
||||
|
||||
t.mutex.Lock()
|
||||
previousInput := t.input
|
||||
|
@@ -712,7 +712,7 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, prev
|
||||
|
||||
func (w *LightWindow) drawBorder() {
|
||||
switch w.border.shape {
|
||||
case BorderRounded, BorderSharp:
|
||||
case BorderRounded, BorderSharp, BorderBold, BorderDouble:
|
||||
w.drawBorderAround()
|
||||
case BorderHorizontal:
|
||||
w.drawBorderHorizontal(true, true)
|
||||
|
@@ -75,8 +75,6 @@ func (w *TcellWindow) Refresh() {
|
||||
}
|
||||
w.lastX = 0
|
||||
w.lastY = 0
|
||||
|
||||
w.drawBorder()
|
||||
}
|
||||
|
||||
func (w *TcellWindow) FinishFill() {
|
||||
@@ -517,7 +515,7 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
|
||||
if preview {
|
||||
normal = ColPreview
|
||||
}
|
||||
return &TcellWindow{
|
||||
w := &TcellWindow{
|
||||
color: r.theme.Colored,
|
||||
preview: preview,
|
||||
top: top,
|
||||
@@ -526,6 +524,8 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
|
||||
height: height,
|
||||
normal: normal,
|
||||
borderStyle: borderStyle}
|
||||
w.drawBorder()
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *TcellWindow) Close() {
|
||||
@@ -705,31 +705,31 @@ func (w *TcellWindow) drawBorder() {
|
||||
}
|
||||
|
||||
switch shape {
|
||||
case BorderRounded, BorderSharp, BorderHorizontal, BorderTop:
|
||||
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderHorizontal, BorderTop:
|
||||
for x := left; x < right; x++ {
|
||||
_screen.SetContent(x, top, w.borderStyle.horizontal, nil, style)
|
||||
}
|
||||
}
|
||||
switch shape {
|
||||
case BorderRounded, BorderSharp, BorderHorizontal, BorderBottom:
|
||||
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderHorizontal, BorderBottom:
|
||||
for x := left; x < right; x++ {
|
||||
_screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style)
|
||||
}
|
||||
}
|
||||
switch shape {
|
||||
case BorderRounded, BorderSharp, BorderVertical, BorderLeft:
|
||||
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderVertical, BorderLeft:
|
||||
for y := top; y < bot; y++ {
|
||||
_screen.SetContent(left, y, w.borderStyle.vertical, nil, style)
|
||||
}
|
||||
}
|
||||
switch shape {
|
||||
case BorderRounded, BorderSharp, BorderVertical, BorderRight:
|
||||
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderVertical, BorderRight:
|
||||
for y := top; y < bot; y++ {
|
||||
_screen.SetContent(right-1, y, w.borderStyle.vertical, nil, style)
|
||||
}
|
||||
}
|
||||
switch shape {
|
||||
case BorderRounded, BorderSharp:
|
||||
case BorderRounded, BorderSharp, BorderBold, BorderDouble:
|
||||
_screen.SetContent(left, top, w.borderStyle.topLeft, nil, style)
|
||||
_screen.SetContent(right-1, top, w.borderStyle.topRight, nil, style)
|
||||
_screen.SetContent(left, bot-1, w.borderStyle.bottomLeft, nil, style)
|
||||
|
@@ -90,6 +90,7 @@ const (
|
||||
|
||||
Change
|
||||
BackwardEOF
|
||||
Start
|
||||
|
||||
AltBS
|
||||
|
||||
@@ -266,7 +267,9 @@ type ColorTheme struct {
|
||||
Cursor ColorAttr
|
||||
Selected ColorAttr
|
||||
Header ColorAttr
|
||||
Separator ColorAttr
|
||||
Border ColorAttr
|
||||
BorderLabel ColorAttr
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
@@ -291,6 +294,8 @@ const (
|
||||
BorderNone BorderShape = iota
|
||||
BorderRounded
|
||||
BorderSharp
|
||||
BorderBold
|
||||
BorderDouble
|
||||
BorderHorizontal
|
||||
BorderVertical
|
||||
BorderTop
|
||||
@@ -312,18 +317,19 @@ type BorderStyle struct {
|
||||
type BorderCharacter int
|
||||
|
||||
func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
||||
if unicode {
|
||||
if shape == BorderRounded {
|
||||
return BorderStyle{
|
||||
shape: shape,
|
||||
horizontal: '─',
|
||||
vertical: '│',
|
||||
topLeft: '╭',
|
||||
topRight: '╮',
|
||||
bottomLeft: '╰',
|
||||
bottomRight: '╯',
|
||||
}
|
||||
if !unicode {
|
||||
return BorderStyle{
|
||||
shape: shape,
|
||||
horizontal: '-',
|
||||
vertical: '|',
|
||||
topLeft: '+',
|
||||
topRight: '+',
|
||||
bottomLeft: '+',
|
||||
bottomRight: '+',
|
||||
}
|
||||
}
|
||||
switch shape {
|
||||
case BorderSharp:
|
||||
return BorderStyle{
|
||||
shape: shape,
|
||||
horizontal: '─',
|
||||
@@ -333,15 +339,35 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
||||
bottomLeft: '└',
|
||||
bottomRight: '┘',
|
||||
}
|
||||
case BorderBold:
|
||||
return BorderStyle{
|
||||
shape: shape,
|
||||
horizontal: '━',
|
||||
vertical: '┃',
|
||||
topLeft: '┏',
|
||||
topRight: '┓',
|
||||
bottomLeft: '┗',
|
||||
bottomRight: '┛',
|
||||
}
|
||||
case BorderDouble:
|
||||
return BorderStyle{
|
||||
shape: shape,
|
||||
horizontal: '═',
|
||||
vertical: '║',
|
||||
topLeft: '╔',
|
||||
topRight: '╗',
|
||||
bottomLeft: '╚',
|
||||
bottomRight: '╝',
|
||||
}
|
||||
}
|
||||
return BorderStyle{
|
||||
shape: shape,
|
||||
horizontal: '-',
|
||||
vertical: '|',
|
||||
topLeft: '+',
|
||||
topRight: '+',
|
||||
bottomLeft: '+',
|
||||
bottomRight: '+',
|
||||
horizontal: '─',
|
||||
vertical: '│',
|
||||
topLeft: '╭',
|
||||
topRight: '╮',
|
||||
bottomLeft: '╰',
|
||||
bottomRight: '╯',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,9 +463,11 @@ var (
|
||||
ColSpinner ColorPair
|
||||
ColInfo ColorPair
|
||||
ColHeader ColorPair
|
||||
ColSeparator ColorPair
|
||||
ColBorder ColorPair
|
||||
ColPreview ColorPair
|
||||
ColPreviewBorder ColorPair
|
||||
ColBorderLabel ColorPair
|
||||
)
|
||||
|
||||
func EmptyTheme() *ColorTheme {
|
||||
@@ -462,7 +490,10 @@ func EmptyTheme() *ColorTheme {
|
||||
Cursor: ColorAttr{colUndefined, AttrUndefined},
|
||||
Selected: ColorAttr{colUndefined, AttrUndefined},
|
||||
Header: ColorAttr{colUndefined, AttrUndefined},
|
||||
Border: ColorAttr{colUndefined, AttrUndefined}}
|
||||
Separator: ColorAttr{colUndefined, AttrUndefined},
|
||||
Border: ColorAttr{colUndefined, AttrUndefined},
|
||||
BorderLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||
}
|
||||
}
|
||||
|
||||
func NoColorTheme() *ColorTheme {
|
||||
@@ -485,7 +516,10 @@ func NoColorTheme() *ColorTheme {
|
||||
Cursor: ColorAttr{colDefault, AttrRegular},
|
||||
Selected: ColorAttr{colDefault, AttrRegular},
|
||||
Header: ColorAttr{colDefault, AttrRegular},
|
||||
Border: ColorAttr{colDefault, AttrRegular}}
|
||||
Separator: ColorAttr{colDefault, AttrRegular},
|
||||
Border: ColorAttr{colDefault, AttrRegular},
|
||||
BorderLabel: ColorAttr{colDefault, AttrRegular},
|
||||
}
|
||||
}
|
||||
|
||||
func errorExit(message string) {
|
||||
@@ -513,7 +547,10 @@ func init() {
|
||||
Cursor: ColorAttr{colRed, AttrUndefined},
|
||||
Selected: ColorAttr{colMagenta, AttrUndefined},
|
||||
Header: ColorAttr{colCyan, AttrUndefined},
|
||||
Border: ColorAttr{colBlack, AttrUndefined}}
|
||||
Separator: ColorAttr{colBlack, AttrUndefined},
|
||||
Border: ColorAttr{colBlack, AttrUndefined},
|
||||
BorderLabel: ColorAttr{colWhite, AttrUndefined},
|
||||
}
|
||||
Dark256 = &ColorTheme{
|
||||
Colored: true,
|
||||
Input: ColorAttr{colDefault, AttrUndefined},
|
||||
@@ -533,7 +570,10 @@ func init() {
|
||||
Cursor: ColorAttr{161, AttrUndefined},
|
||||
Selected: ColorAttr{168, AttrUndefined},
|
||||
Header: ColorAttr{109, AttrUndefined},
|
||||
Border: ColorAttr{59, AttrUndefined}}
|
||||
Separator: ColorAttr{59, AttrUndefined},
|
||||
Border: ColorAttr{59, AttrUndefined},
|
||||
BorderLabel: ColorAttr{145, AttrUndefined},
|
||||
}
|
||||
Light256 = &ColorTheme{
|
||||
Colored: true,
|
||||
Input: ColorAttr{colDefault, AttrUndefined},
|
||||
@@ -553,7 +593,10 @@ func init() {
|
||||
Cursor: ColorAttr{161, AttrUndefined},
|
||||
Selected: ColorAttr{168, AttrUndefined},
|
||||
Header: ColorAttr{31, AttrUndefined},
|
||||
Border: ColorAttr{145, AttrUndefined}}
|
||||
Separator: ColorAttr{145, AttrUndefined},
|
||||
Border: ColorAttr{145, AttrUndefined},
|
||||
BorderLabel: ColorAttr{59, AttrUndefined},
|
||||
}
|
||||
}
|
||||
|
||||
func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
|
||||
@@ -588,7 +631,9 @@ func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
|
||||
theme.Cursor = o(baseTheme.Cursor, theme.Cursor)
|
||||
theme.Selected = o(baseTheme.Selected, theme.Selected)
|
||||
theme.Header = o(baseTheme.Header, theme.Header)
|
||||
theme.Separator = o(baseTheme.Separator, theme.Separator)
|
||||
theme.Border = o(baseTheme.Border, theme.Border)
|
||||
theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel)
|
||||
|
||||
initPalette(theme)
|
||||
}
|
||||
@@ -620,7 +665,9 @@ func initPalette(theme *ColorTheme) {
|
||||
ColSpinner = pair(theme.Spinner, theme.Bg)
|
||||
ColInfo = pair(theme.Info, theme.Bg)
|
||||
ColHeader = pair(theme.Header, theme.Bg)
|
||||
ColSeparator = pair(theme.Separator, theme.Bg)
|
||||
ColBorder = pair(theme.Border, theme.Bg)
|
||||
ColBorderLabel = pair(theme.BorderLabel, theme.Bg)
|
||||
ColPreview = pair(theme.PreviewFg, theme.PreviewBg)
|
||||
ColPreviewBorder = pair(theme.Border, theme.PreviewBg)
|
||||
}
|
||||
|
@@ -153,3 +153,23 @@ func Once(nextResponse bool) func() bool {
|
||||
return prevState
|
||||
}
|
||||
}
|
||||
|
||||
// RepeatToFill repeats the given string to fill the given width
|
||||
func RepeatToFill(str string, length int, limit int) string {
|
||||
times := limit / length
|
||||
rest := limit % length
|
||||
output := strings.Repeat(str, times)
|
||||
if rest > 0 {
|
||||
for _, r := range str {
|
||||
rest -= runewidth.RuneWidth(r)
|
||||
if rest < 0 {
|
||||
break
|
||||
}
|
||||
output += string(r)
|
||||
if rest == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
@@ -192,6 +192,13 @@ class TestBase < Minitest::Test
|
||||
tmux.prepare
|
||||
end
|
||||
|
||||
alias assert_equal_org assert_equal
|
||||
def assert_equal(expected, actual)
|
||||
# Ignore info separator
|
||||
actual = actual&.sub(/\s*─+$/, '') if actual.is_a?(String) && actual&.match?(%r{\d+/\d+})
|
||||
assert_equal_org(expected, actual)
|
||||
end
|
||||
|
||||
def fzf(*opts)
|
||||
fzf!(*opts) + " > #{tempname}.tmp; mv #{tempname}.tmp #{tempname}"
|
||||
end
|
||||
@@ -255,7 +262,7 @@ class TestGoFZF < TestBase
|
||||
|
||||
def test_fzf_default_command_failure
|
||||
tmux.send_keys fzf.sub('FZF_DEFAULT_COMMAND=', 'FZF_DEFAULT_COMMAND=false'), :Enter
|
||||
tmux.until { |lines| assert_equal ' [Command failed: false]', lines[-2] }
|
||||
tmux.until { |lines| assert_includes lines[-2], ' [Command failed: false] ─' }
|
||||
tmux.send_keys :Enter
|
||||
end
|
||||
|
||||
@@ -447,7 +454,7 @@ class TestGoFZF < TestBase
|
||||
def test_scroll
|
||||
[true, false].each do |rev|
|
||||
tmux.send_keys "seq 1 100 | #{fzf(rev && :reverse)}", :Enter
|
||||
tmux.until { |lines| assert_includes lines, ' 100/100' }
|
||||
tmux.until { |lines| assert_equal ' 100/100', lines[rev ? 1 : -2] }
|
||||
tmux.send_keys(*Array.new(110) { rev ? :Down : :Up })
|
||||
tmux.until { |lines| assert_includes lines, '> 100' }
|
||||
tmux.send_keys :Enter
|
||||
@@ -2249,7 +2256,7 @@ class TestGoFZF < TestBase
|
||||
def assert_block(expected, lines)
|
||||
cols = expected.lines.map(&:chomp).map(&:length).max
|
||||
actual = lines.reverse.take(expected.lines.length).reverse.map { _1[0, cols].rstrip + "\n" }.join
|
||||
assert_equal expected, actual
|
||||
assert_equal_org expected, actual
|
||||
end
|
||||
|
||||
def test_height_range_fit
|
||||
@@ -2297,7 +2304,7 @@ class TestGoFZF < TestBase
|
||||
│ 2
|
||||
│ 1
|
||||
│ hello
|
||||
│ 1/1
|
||||
│ 1/1 ─
|
||||
│ >
|
||||
│
|
||||
└─────────
|
||||
@@ -2314,7 +2321,7 @@ class TestGoFZF < TestBase
|
||||
│ 3 │ 1
|
||||
│ │ hello
|
||||
│ │ world
|
||||
│ │ 1/1
|
||||
│ │ 1/1 ─
|
||||
│ │>
|
||||
│
|
||||
OUTPUT
|
||||
@@ -2332,6 +2339,71 @@ class TestGoFZF < TestBase
|
||||
OUTPUT
|
||||
tmux.until { assert_block(expected, _1) }
|
||||
end
|
||||
|
||||
def test_start_event
|
||||
tmux.send_keys 'seq 100 | fzf --multi --sync --preview-window border-none --bind "start:select-all+last+preview(echo welcome)"', :Enter
|
||||
tmux.until do |lines|
|
||||
assert_match(/>100.*welcome/, lines[0])
|
||||
assert_includes(lines[-2], '100/100 (100)')
|
||||
end
|
||||
end
|
||||
|
||||
def test_labels_center
|
||||
tmux.send_keys ': | fzf --border --border-label foobar --preview : --preview-label barfoo', :Enter
|
||||
tmux.until do
|
||||
assert_includes(_1[0], '─foobar─')
|
||||
assert_includes(_1[1], '─barfoo─')
|
||||
end
|
||||
end
|
||||
|
||||
def test_labels_left
|
||||
tmux.send_keys ': | fzf --border --border-label foobar --border-label-pos 2 --preview : --preview-label barfoo --preview-label-pos 2', :Enter
|
||||
tmux.until do
|
||||
assert_includes(_1[0], '╭foobar─')
|
||||
assert_includes(_1[1], '╭barfoo─')
|
||||
end
|
||||
end
|
||||
|
||||
def test_labels_right
|
||||
tmux.send_keys ': | fzf --border --border-label foobar --border-label-pos -2 --preview : --preview-label barfoo --preview-label-pos -2', :Enter
|
||||
tmux.until do
|
||||
assert_includes(_1[0], '─foobar╮')
|
||||
assert_includes(_1[1], '─barfoo╮')
|
||||
end
|
||||
end
|
||||
|
||||
def test_labels_bottom
|
||||
tmux.send_keys ': | fzf --border --border-label foobar --border-label-pos 2:bottom --preview : --preview-label barfoo --preview-label-pos -2:bottom', :Enter
|
||||
tmux.until do
|
||||
assert_includes(_1[-1], '╰foobar─')
|
||||
assert_includes(_1[-2], '─barfoo╯')
|
||||
end
|
||||
end
|
||||
|
||||
def test_info_separator_unicode
|
||||
tmux.send_keys 'seq 100 | fzf -q55', :Enter
|
||||
tmux.until { assert_includes(_1[-2], ' 1/100 ─') }
|
||||
end
|
||||
|
||||
def test_info_separator_no_unicode
|
||||
tmux.send_keys 'seq 100 | fzf -q55 --no-unicode', :Enter
|
||||
tmux.until { assert_includes(_1[-2], ' 1/100 -') }
|
||||
end
|
||||
|
||||
def test_info_separator_repeat
|
||||
tmux.send_keys 'seq 100 | fzf -q55 --separator _-', :Enter
|
||||
tmux.until { assert_includes(_1[-2], ' 1/100 _-_-') }
|
||||
end
|
||||
|
||||
def test_info_separator_ansi_colors_and_tabs
|
||||
tmux.send_keys "seq 100 | fzf -q55 --tabstop 4 --separator $'\\x1b[33ma\\tb'", :Enter
|
||||
tmux.until { assert_includes(_1[-2], ' 1/100 a ba ba') }
|
||||
end
|
||||
|
||||
def test_info_no_separator
|
||||
tmux.send_keys 'seq 100 | fzf -q55 --no-separator', :Enter
|
||||
tmux.until { assert(_1[-2] == ' 1/100') }
|
||||
end
|
||||
end
|
||||
|
||||
module TestShell
|
||||
@@ -2758,6 +2830,7 @@ class TestFish < TestBase
|
||||
end
|
||||
|
||||
__END__
|
||||
set -u
|
||||
PS1= PROMPT_COMMAND= HISTFILE= HISTSIZE=100
|
||||
unset <%= UNSETS.join(' ') %>
|
||||
unset $(env | sed -n /^_fzf_orig/s/=.*//p)
|
||||
@@ -2801,8 +2874,8 @@ _fzf_complete_g_post() {
|
||||
awk '{print "g" $0 $0}'
|
||||
}
|
||||
|
||||
[ -n "$BASH" ] && complete -F _fzf_complete_f -o default -o bashdefault f
|
||||
[ -n "$BASH" ] && complete -F _fzf_complete_g -o default -o bashdefault g
|
||||
[ -n "${BASH-}" ] && complete -F _fzf_complete_f -o default -o bashdefault f
|
||||
[ -n "${BASH-}" ] && complete -F _fzf_complete_g -o default -o bashdefault g
|
||||
|
||||
_comprun() {
|
||||
local command=$1
|
||||
|
Reference in New Issue
Block a user