m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-15 06:43:47 -05:00

Compare commits

...

16 Commits

Author SHA1 Message Date
Junegunn Choi
98d2bfa0db [install] Terminate install script when failed to update shell config
Close #354
2015-09-24 10:51:05 +09:00
Junegunn Choi
aec48f159b [neovim] Remove redraw! hack that is no longer needed 2015-09-22 16:36:48 +09:00
Junegunn Choi
ad7e433a7d Use build tags to enable static linking 2015-09-22 13:16:50 +09:00
Junegunn Choi
5a60aa5050 [vim] Display proper error message when command failed 2015-09-20 14:10:43 +09:00
Junegunn Choi
ebea470875 Build linux binary on Centos 2015-09-20 00:17:44 +09:00
Junegunn Choi
d980e00961 Revert "Revert "0.10.6""
This reverts commit 987799f8fb.
2015-09-19 22:51:12 +09:00
Junegunn Choi
987799f8fb Revert "0.10.6"
This reverts commit d2f3604c1d.
2015-09-19 22:27:09 +09:00
Junegunn Choi
d2f3604c1d 0.10.6 2015-09-19 22:18:04 +09:00
Junegunn Choi
72cc558fdc Fix travis CI build 2015-09-19 18:39:09 +09:00
Junegunn Choi
6bc3fe6e67 Build partially-static binary for linux (#350)
Instead of building a separate statically-linked binary, build
partially-static binary that only contains ncurses to avoid
compatibility issues in libc.
2015-09-19 18:33:25 +09:00
Junegunn Choi
9398878048 [fzf-tmux] Exit with the same exit status as with fzf 2015-09-18 10:28:09 +09:00
Junegunn Choi
ca19762e58 Exit status 130 when fzf is terminated by the user
Related: #345
2015-09-18 10:25:07 +09:00
Junegunn Choi
8764be07e2 [vim] Ignore exit status of 2 (#345) 2015-09-18 09:59:40 +09:00
Junegunn Choi
2022a3ad96 Replace --header-file with --header (#346)
and allow using --header and --header-lines at the same time.

Close #346.
2015-09-15 19:04:53 +09:00
Junegunn Choi
65d9d416b4 Change exit status (0: OK, 1: No match, 2: Error/Interrupted)
A la grep. Close #345
2015-09-15 13:21:51 +09:00
Junegunn Choi
fa2f9f1f21 Remove flattr badge 2015-09-14 15:49:19 +09:00
16 changed files with 216 additions and 121 deletions

View File

@@ -4,7 +4,7 @@ rvm:
install: install:
- sudo apt-get update - sudo apt-get update
- sudo apt-get install -y libncurses-dev lib32ncurses5-dev - sudo apt-get install -y libncurses-dev lib32ncurses5-dev libgpm-dev
- sudo add-apt-repository -y ppa:pi-rho/dev - sudo add-apt-repository -y ppa:pi-rho/dev
- sudo apt-add-repository -y ppa:fish-shell/release-2 - sudo apt-add-repository -y ppa:fish-shell/release-2
- sudo apt-get update - sudo apt-get update

View File

@@ -1,6 +1,19 @@
CHANGELOG CHANGELOG
========= =========
0.10.6
------
- Replaced `--header-file` with `--header` option
- `--header` and `--header-lines` can be used together
- Changed exit status
- 0: Okay
- 1: No match
- 2: Error
- 130: Interrupted
- 64-bit linux binary is statically-linked with ncurses to avoid
compatibility issues.
0.10.5 0.10.5
------ ------

View File

@@ -1,4 +1,4 @@
<img src="https://raw.githubusercontent.com/junegunn/i/master/fzf.png" height="170" alt="fzf - a command-line fuzzy finder"> [![travis-ci](https://travis-ci.org/junegunn/fzf.svg?branch=master)](https://travis-ci.org/junegunn/fzf) <a href="http://flattr.com/thing/3115381/junegunnfzf-on-GitHub" target="_blank"><img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a> <img src="https://raw.githubusercontent.com/junegunn/i/master/fzf.png" height="170" alt="fzf - a command-line fuzzy finder"> [![travis-ci](https://travis-ci.org/junegunn/fzf.svg?branch=master)](https://travis-ci.org/junegunn/fzf)
=== ===
fzf is a general-purpose command-line fuzzy finder. fzf is a general-purpose command-line fuzzy finder.

View File

@@ -102,7 +102,7 @@ trap cleanup EXIT SIGINT SIGTERM
fail() { fail() {
>&2 echo "$1" >&2 echo "$1"
exit 1 exit 2
} }
fzf="$(which fzf 2> /dev/null)" || fzf="$(dirname "$0")/fzf" fzf="$(which fzf 2> /dev/null)" || fzf="$(dirname "$0")/fzf"
[ -x "$fzf" ] || fail "fzf executable not found" [ -x "$fzf" ] || fail "fzf executable not found"
@@ -134,5 +134,5 @@ else
cat <&0 > $fifo1 & cat <&0 > $fifo1 &
fi fi
cat $fifo2 cat $fifo2
[ "$(cat $fifo3)" = '0' ] exit "$(cat $fifo3)"

13
install
View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
[[ "$@" =~ --pre ]] && version=0.10.5 pre=1 || [[ "$@" =~ --pre ]] && version=0.10.6 pre=1 ||
version=0.10.5 pre=0 version=0.10.6 pre=0
cd $(dirname $BASH_SOURCE) cd $(dirname $BASH_SOURCE)
fzf_base=$(pwd) fzf_base=$(pwd)
@@ -80,12 +80,7 @@ download() {
return return
fi fi
chmod +x $1 && symlink $1 || return 1 chmod +x $1 && symlink $1 && check_binary
if [[ $1 =~ linux_amd64$ ]]; then
check_binary || download $1-static
else
check_binary
fi
} }
# Try to download binary executable # Try to download binary executable
@@ -250,6 +245,7 @@ EOF
fi fi
append_line() { append_line() {
set -e
echo "Update $2:" echo "Update $2:"
echo " - $1" echo " - $1"
[ -f "$2" ] || touch "$2" [ -f "$2" ] || touch "$2"
@@ -266,6 +262,7 @@ append_line() {
echo " + Added" echo " + Added"
fi fi
echo echo
set +e
} }
echo echo

View File

@@ -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 "Sep 2015" "fzf 0.10.5" "fzf - a command-line fuzzy finder" .TH fzf 1 "Sep 2015" "fzf 0.10.6" "fzf - a command-line fuzzy finder"
.SH NAME .SH NAME
fzf - a command-line fuzzy finder fzf - a command-line fuzzy finder
@@ -285,11 +285,11 @@ When enabled, \fBCTRL-N\fR and \fBCTRL-P\fR are automatically remapped to
Maximum number of entries in the history file (default: 1000). The file is Maximum number of entries in the history file (default: 1000). The file is
automatically truncated when the number of the lines exceeds the value. automatically truncated when the number of the lines exceeds the value.
.TP .TP
.BI "--header-file=" "FILE" .BI "--header=" "STR"
The content of the file will be printed as the sticky header. The lines in the The given string will be printed as the sticky header. The lines are displayed
file are displayed in order from top to bottom regardless of \fB--reverse\fR, in the given order from top to bottom regardless of \fB--reverse\fR option, and
and are not affected by \fB--with-nth\fR. ANSI color codes are processed even are not affected by \fB--with-nth\fR. ANSI color codes are processed even when
when \fB--ansi\fR is not set. \fB--ansi\fR is not set.
.TP .TP
.BI "--header-lines=" "N" .BI "--header-lines=" "N"
The first N lines of the input are treated as the sticky header. When The first N lines of the input are treated as the sticky header. When

View File

@@ -40,9 +40,7 @@ function! s:fzf_exec()
\ input('fzf executable not found. Download binary? (y/n) ') =~? '^y' \ input('fzf executable not found. Download binary? (y/n) ') =~? '^y'
redraw redraw
echo echo
echohl WarningMsg call s:warn('Downloading fzf binary. Please wait ...')
echo 'Downloading fzf binary. Please wait ...'
echohl None
let s:installed = 1 let s:installed = 1
call system(s:install.' --bin') call system(s:install.' --bin')
return s:fzf_exec() return s:fzf_exec()
@@ -98,14 +96,24 @@ function! s:upgrade(dict)
return copy return copy
endfunction endfunction
function! s:error(msg)
echohl ErrorMsg
echom a:msg
echohl None
endfunction
function! s:warn(msg)
echohl WarningMsg
echom a:msg
echohl None
endfunction
function! fzf#run(...) abort function! fzf#run(...) abort
try try
let oshell = &shell let oshell = &shell
set shell=sh set shell=sh
if has('nvim') && bufexists('term://*:FZF') if has('nvim') && bufexists('term://*:FZF')
echohl WarningMsg call s:warn('FZF is already running!')
echomsg 'FZF is already running!'
echohl None
return [] return []
endif endif
let dict = exists('a:1') ? s:upgrade(a:1) : {} let dict = exists('a:1') ? s:upgrade(a:1) : {}
@@ -228,10 +236,9 @@ function! s:execute(dict, command, temps)
execute 'silent !'.command execute 'silent !'.command
redraw! redraw!
if v:shell_error if v:shell_error
" Do not print error message on exit status 1 " Do not print error message on exit status 1 (no match) or 130 (interrupt)
if v:shell_error > 1 if v:shell_error == 2
echohl ErrorMsg call s:error('Error running ' . command)
echo 'Error running ' . command
endif endif
return [] return []
else else
@@ -248,6 +255,10 @@ function! s:execute_tmux(dict, command, temps)
call system(command) call system(command)
redraw! redraw!
if v:shell_error == 2
call s:error('Error running ' . command)
return []
endif
return s:callback(a:dict, a:temps) return s:callback(a:dict, a:temps)
endfunction endfunction
@@ -309,6 +320,7 @@ function! s:execute_term(dict, command, temps)
call s:pushd(a:dict) call s:pushd(a:dict)
let fzf = { 'buf': bufnr('%'), 'dict': a:dict, 'temps': a:temps, 'name': 'FZF' } let fzf = { 'buf': bufnr('%'), 'dict': a:dict, 'temps': a:temps, 'name': 'FZF' }
let s:command = a:command
function! fzf.on_exit(id, code) function! fzf.on_exit(id, code)
let pos = s:getpos() let pos = s:getpos()
let inplace = pos == s:ppos " {'window': 'enew'} let inplace = pos == s:ppos " {'window': 'enew'}
@@ -322,9 +334,15 @@ function! s:execute_term(dict, command, temps)
wincmd p wincmd p
endif endif
endif endif
if a:code == 2
call s:error('Error running ' . s:command)
sleep
return
endif
call s:pushd(self.dict) call s:pushd(self.dict)
try try
redraw!
call s:callback(self.dict, self.temps) call s:callback(self.dict, self.temps)
if inplace && bufnr('') == self.buf if inplace && bufnr('') == self.buf

View File

@@ -49,7 +49,7 @@ _fzf_opts_completion() {
--cycle --cycle
--history --history
--history-size --history-size
--header-file --header
--header-lines --header-lines
--margin" --margin"
@@ -62,7 +62,7 @@ _fzf_opts_completion() {
COMPREPLY=( $(compgen -W "dark light 16 bw" -- ${cur}) ) COMPREPLY=( $(compgen -W "dark light 16 bw" -- ${cur}) )
return 0 return 0
;; ;;
--history|--header-file) --history)
COMPREPLY=() COMPREPLY=()
return 0 return 0
;; ;;

View File

@@ -1,8 +1,10 @@
FROM centos:centos7 FROM centos:centos6
MAINTAINER Junegunn Choi <junegunn.c@gmail.com> MAINTAINER Junegunn Choi <junegunn.c@gmail.com>
# yum # yum
RUN yum install -y git gcc make tar ncurses-devel RUN yum install -y git gcc make tar glibc-devel glibc-devel.i686 \
ncurses-devel ncurses-static ncurses-devel.i686 \
gpm-devel gpm-static libgcc.i686
# Install Go 1.4 # Install Go 1.4
RUN cd / && curl \ RUN cd / && curl \
@@ -13,6 +15,9 @@ ENV GOPATH /go
ENV GOROOT /go1.4 ENV GOROOT /go1.4
ENV PATH /go1.4/bin:$PATH ENV PATH /go1.4/bin:$PATH
# For i386 build
RUN cd $GOROOT/src && GOARCH=386 ./make.bash
# Volume # Volume
VOLUME /go VOLUME /go

View File

@@ -5,19 +5,8 @@ endif
UNAME_S := $(shell uname -s) UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin) ifeq ($(UNAME_S),Darwin)
GOOS := darwin GOOS := darwin
LDFLAGS :=
ifdef STATIC
$(error Static linking not possible on OS X)
endif
else ifeq ($(UNAME_S),Linux) else ifeq ($(UNAME_S),Linux)
GOOS := linux GOOS := linux
ifdef STATIC
SUFFIX := -static
LDFLAGS := --ldflags '-extldflags "-static -ltinfo -lgpm"'
else
SUFFIX :=
LDFLAGS :=
endif
endif endif
ifneq ($(shell uname -m),x86_64) ifneq ($(shell uname -m),x86_64)
@@ -27,11 +16,11 @@ endif
SOURCES := $(wildcard *.go */*.go) SOURCES := $(wildcard *.go */*.go)
BINDIR := ../bin BINDIR := ../bin
BINARY32 := fzf-$(GOOS)_386$(SUFFIX) BINARY32 := fzf-$(GOOS)_386
BINARY64 := fzf-$(GOOS)_amd64$(SUFFIX) BINARY64 := fzf-$(GOOS)_amd64
VERSION = $(shell fzf/$(BINARY64) --version) VERSION = $(shell fzf/$(BINARY64) --version)
RELEASE32 = fzf-$(VERSION)-$(GOOS)_386$(SUFFIX) RELEASE32 = fzf-$(VERSION)-$(GOOS)_386
RELEASE64 = fzf-$(VERSION)-$(GOOS)_amd64$(SUFFIX) RELEASE64 = fzf-$(VERSION)-$(GOOS)_amd64
all: release all: release
@@ -40,11 +29,7 @@ release: build
cd fzf && cp $(BINARY64) $(RELEASE64) && tar -czf $(RELEASE64).tgz $(RELEASE64) && \ cd fzf && cp $(BINARY64) $(RELEASE64) && tar -czf $(RELEASE64).tgz $(RELEASE64) && \
rm -f $(RELEASE32) $(RELEASE64) rm -f $(RELEASE32) $(RELEASE64)
ifndef STATIC
build: test fzf/$(BINARY32) fzf/$(BINARY64) build: test fzf/$(BINARY32) fzf/$(BINARY64)
else
build: test fzf/$(BINARY64)
endif
test: test:
go get go get
@@ -59,10 +44,10 @@ clean:
cd fzf && rm -f fzf-* cd fzf && rm -f fzf-*
fzf/$(BINARY32): $(SOURCES) fzf/$(BINARY32): $(SOURCES)
cd fzf && GOARCH=386 CGO_ENABLED=1 go build -o $(BINARY32) cd fzf && GOARCH=386 CGO_ENABLED=1 go build -a -o $(BINARY32)
fzf/$(BINARY64): $(SOURCES) fzf/$(BINARY64): $(SOURCES)
cd fzf && go build $(LDFLAGS) -o $(BINARY64) cd fzf && go build -a -tags "$(TAGS)" -o $(BINARY64)
$(BINDIR)/fzf: fzf/$(BINARY64) | $(BINDIR) $(BINDIR)/fzf: fzf/$(BINARY64) | $(BINDIR)
cp -f fzf/$(BINARY64) $(BINDIR) cp -f fzf/$(BINARY64) $(BINDIR)
@@ -77,6 +62,9 @@ docker-arch:
docker-ubuntu: docker-ubuntu:
docker build -t junegunn/ubuntu-sandbox - < Dockerfile.ubuntu docker build -t junegunn/ubuntu-sandbox - < Dockerfile.ubuntu
docker-centos:
docker build -t junegunn/centos-sandbox - < Dockerfile.centos
arch: docker-arch arch: docker-arch
docker run -i -t -v $(GOPATH):/go junegunn/$@-sandbox \ docker run -i -t -v $(GOPATH):/go junegunn/$@-sandbox \
sh -c 'cd /go/src/github.com/junegunn/fzf/src; /bin/bash' sh -c 'cd /go/src/github.com/junegunn/fzf/src; /bin/bash'
@@ -85,13 +73,13 @@ ubuntu: docker-ubuntu
docker run -i -t -v $(GOPATH):/go junegunn/$@-sandbox \ docker run -i -t -v $(GOPATH):/go junegunn/$@-sandbox \
sh -c 'cd /go/src/github.com/junegunn/fzf/src; /bin/bash' sh -c 'cd /go/src/github.com/junegunn/fzf/src; /bin/bash'
linux: docker-arch centos: docker-centos
docker run -i -t -v $(GOPATH):/go junegunn/arch-sandbox \ docker run -i -t -v $(GOPATH):/go junegunn/$@-sandbox \
/bin/bash -ci 'cd /go/src/github.com/junegunn/fzf/src; make' sh -c 'cd /go/src/github.com/junegunn/fzf/src; /bin/bash'
linux-static: docker-ubuntu linux: docker-centos
docker run -i -t -v $(GOPATH):/go junegunn/ubuntu-sandbox \ docker run -i -t -v $(GOPATH):/go junegunn/centos-sandbox \
/bin/bash -ci 'cd /go/src/github.com/junegunn/fzf/src; make STATIC=1' /bin/bash -ci 'cd /go/src/github.com/junegunn/fzf/src; make TAGS=static'
.PHONY: all build release test install uninstall clean docker \ .PHONY: all build release test install uninstall clean docker \
linux linux-static arch ubuntu docker-arch docker-ubuntu linux arch ubuntu centos docker-arch docker-ubuntu docker-centos

View File

@@ -8,7 +8,7 @@ import (
const ( const (
// Current version // Current version
version = "0.10.5" version = "0.10.6"
// Core // Core
coordinatorDelayMax time.Duration = 100 * time.Millisecond coordinatorDelayMax time.Duration = 100 * time.Millisecond
@@ -47,3 +47,10 @@ const (
EvtHeader EvtHeader
EvtClose EvtClose
) )
const (
exitOk = 0
exitNoMatch = 1
exitError = 2
exitInterrupt = 130
)

View File

@@ -56,7 +56,7 @@ func Run(opts *Options) {
if opts.Version { if opts.Version {
fmt.Println(version) fmt.Println(version)
os.Exit(0) os.Exit(exitOk)
} }
// Event channel // Event channel
@@ -156,12 +156,14 @@ func Run(opts *Options) {
pattern := patternBuilder([]rune(*opts.Filter)) pattern := patternBuilder([]rune(*opts.Filter))
found := false
if streamingFilter { if streamingFilter {
reader := Reader{ reader := Reader{
func(runes []byte) bool { func(runes []byte) bool {
item := chunkList.trans(runes, 0) item := chunkList.trans(runes, 0)
if item != nil && pattern.MatchItem(item) { if item != nil && pattern.MatchItem(item) {
fmt.Println(string(item.text)) fmt.Println(string(item.text))
found = true
} }
return false return false
}, eventBox, opts.ReadZero} }, eventBox, opts.ReadZero}
@@ -176,9 +178,13 @@ func Run(opts *Options) {
pattern: pattern}) pattern: pattern})
for i := 0; i < merger.Length(); i++ { for i := 0; i < merger.Length(); i++ {
fmt.Println(merger.Get(i).AsString(opts.Ansi)) fmt.Println(merger.Get(i).AsString(opts.Ansi))
found = true
} }
} }
os.Exit(0) if found {
os.Exit(exitOk)
}
os.Exit(exitNoMatch)
} }
// Synchronous search // Synchronous search
@@ -232,7 +238,7 @@ func Run(opts *Options) {
} }
case EvtHeader: case EvtHeader:
terminal.UpdateHeader(value.([]string), opts.HeaderLines) terminal.UpdateHeader(value.([]string))
case EvtSearchFin: case EvtSearchFin:
switch val := value.(type) { switch val := value.(type) {
@@ -253,7 +259,10 @@ func Run(opts *Options) {
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
fmt.Println(val.Get(i).AsString(opts.Ansi)) fmt.Println(val.Get(i).AsString(opts.Ansi))
} }
os.Exit(0) if count > 0 {
os.Exit(exitOk)
}
os.Exit(exitNoMatch)
} }
deferred = false deferred = false
terminal.startChan <- true terminal.startChan <- true

View File

@@ -3,7 +3,8 @@ package curses
/* /*
#include <ncurses.h> #include <ncurses.h>
#include <locale.h> #include <locale.h>
#cgo LDFLAGS: -lncurses #cgo !static LDFLAGS: -lncurses
#cgo static LDFLAGS: -l:libncurses.a -l:libtinfo.a -l:libgpm.a -ldl
*/ */
import "C" import "C"
@@ -261,7 +262,7 @@ func Init(theme *ColorTheme, black bool, mouse bool) {
_screen = C.newterm(nil, C.stderr, C.stdin) _screen = C.newterm(nil, C.stderr, C.stdin)
if _screen == nil { if _screen == nil {
fmt.Println("Invalid $TERM: " + os.Getenv("TERM")) fmt.Println("Invalid $TERM: " + os.Getenv("TERM"))
os.Exit(1) os.Exit(2)
} }
C.set_term(_screen) C.set_term(_screen)
if mouse { if mouse {
@@ -275,7 +276,7 @@ func Init(theme *ColorTheme, black bool, mouse bool) {
go func() { go func() {
<-intChan <-intChan
Close() Close()
os.Exit(1) os.Exit(2)
}() }()
if theme != nil { if theme != nil {

View File

@@ -1,7 +1,6 @@
package fzf package fzf
import ( import (
"io/ioutil"
"os" "os"
"regexp" "regexp"
"strconv" "strconv"
@@ -45,7 +44,7 @@ const usage = `usage: fzf [options]
--bind=KEYBINDS Custom key bindings. Refer to the man page. --bind=KEYBINDS Custom key bindings. Refer to the man page.
--history=FILE History file --history=FILE History file
--history-size=N Maximum number of history entries (default: 1000) --history-size=N Maximum number of history entries (default: 1000)
--header-file=FILE The file whose content to be printed 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
Scripting Scripting
@@ -180,14 +179,14 @@ func defaultOptions() *Options {
Version: false} Version: false}
} }
func help(ok int) { func help(code int) {
os.Stderr.WriteString(usage) os.Stderr.WriteString(usage)
os.Exit(ok) os.Exit(code)
} }
func errorExit(msg string) { func errorExit(msg string) {
os.Stderr.WriteString(msg + "\n") os.Stderr.WriteString(msg + "\n")
os.Exit(1) os.Exit(exitError)
} }
func optString(arg string, prefixes ...string) (bool, string) { func optString(arg string, prefixes ...string) (bool, string) {
@@ -604,12 +603,8 @@ func checkToggleSort(keymap map[int]actionType, str string) map[int]actionType {
return keymap return keymap
} }
func readHeaderFile(filename string) []string { func strLines(str string) []string {
content, err := ioutil.ReadFile(filename) return strings.Split(strings.TrimSuffix(str, "\n"), "\n")
if err != nil {
errorExit("failed to read header file: " + filename)
}
return strings.Split(strings.TrimSuffix(string(content), "\n"), "\n")
} }
func parseMargin(margin string) [4]string { func parseMargin(margin string) [4]string {
@@ -682,7 +677,7 @@ func parseOptions(opts *Options, allArgs []string) {
arg := allArgs[i] arg := allArgs[i]
switch arg { switch arg {
case "-h", "--help": case "-h", "--help":
help(0) help(exitOk)
case "-x", "--extended": case "-x", "--extended":
opts.Mode = ModeExtended opts.Mode = ModeExtended
case "-e", "--extended-exact": case "-e", "--extended-exact":
@@ -793,16 +788,13 @@ func parseOptions(opts *Options, allArgs []string) {
setHistory(nextString(allArgs, &i, "history file path required")) setHistory(nextString(allArgs, &i, "history file path required"))
case "--history-size": case "--history-size":
setHistoryMax(nextInt(allArgs, &i, "history max size required")) setHistoryMax(nextInt(allArgs, &i, "history max size required"))
case "--no-header-file": case "--no-header":
opts.Header = []string{} opts.Header = []string{}
case "--no-header-lines": case "--no-header-lines":
opts.HeaderLines = 0 opts.HeaderLines = 0
case "--header-file": case "--header":
opts.Header = readHeaderFile( opts.Header = strLines(nextString(allArgs, &i, "header string required"))
nextString(allArgs, &i, "header file name required"))
opts.HeaderLines = 0
case "--header-lines": case "--header-lines":
opts.Header = []string{}
opts.HeaderLines = atoi( opts.HeaderLines = atoi(
nextString(allArgs, &i, "number of header lines required")) nextString(allArgs, &i, "number of header lines required"))
case "--no-margin": case "--no-margin":
@@ -843,11 +835,9 @@ func parseOptions(opts *Options, allArgs []string) {
setHistory(value) setHistory(value)
} else if match, value := optString(arg, "--history-size="); match { } else if match, value := optString(arg, "--history-size="); match {
setHistoryMax(atoi(value)) setHistoryMax(atoi(value))
} else if match, value := optString(arg, "--header-file="); match { } else if match, value := optString(arg, "--header="); match {
opts.Header = readHeaderFile(value) opts.Header = strLines(value)
opts.HeaderLines = 0
} else if match, value := optString(arg, "--header-lines="); match { } else if match, value := optString(arg, "--header-lines="); match {
opts.Header = []string{}
opts.HeaderLines = atoi(value) opts.HeaderLines = atoi(value)
} else if match, value := optString(arg, "--margin="); match { } else if match, value := optString(arg, "--margin="); match {
opts.Margin = parseMargin(value) opts.Margin = parseMargin(value)

View File

@@ -42,6 +42,7 @@ type Terminal struct {
history *History history *History
cycle bool cycle bool
header []string header []string
header0 []string
ansi bool ansi bool
margin [4]string margin [4]string
marginInt [4]int marginInt [4]int
@@ -185,6 +186,12 @@ func defaultKeymap() map[int]actionType {
// 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 := []rune(opts.Query) input := []rune(opts.Query)
var header []string
if opts.Reverse {
header = opts.Header
} else {
header = reverseStringArray(opts.Header)
}
return &Terminal{ return &Terminal{
inlineInfo: opts.InlineInfo, inlineInfo: opts.InlineInfo,
prompt: opts.Prompt, prompt: opts.Prompt,
@@ -207,7 +214,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
margin: opts.Margin, margin: opts.Margin,
marginInt: [4]int{0, 0, 0, 0}, marginInt: [4]int{0, 0, 0, 0},
cycle: opts.Cycle, cycle: opts.Cycle,
header: opts.Header, header: header,
header0: header,
ansi: opts.Ansi, ansi: opts.Ansi,
reading: true, reading: true,
merger: EmptyMerger, merger: EmptyMerger,
@@ -241,18 +249,19 @@ func (t *Terminal) UpdateCount(cnt int, final bool) {
} }
} }
func reverseStringArray(input []string) []string {
size := len(input)
reversed := make([]string, size)
for idx, str := range input {
reversed[size-idx-1] = str
}
return reversed
}
// UpdateHeader updates the header // UpdateHeader updates the header
func (t *Terminal) UpdateHeader(header []string, lines int) { func (t *Terminal) UpdateHeader(header []string) {
t.mutex.Lock() t.mutex.Lock()
t.header = make([]string, lines) t.header = append(append([]string{}, t.header0...), header...)
copy(t.header, header)
if !t.reverse {
reversed := make([]string, lines)
for idx, str := range t.header {
reversed[lines-idx-1] = str
}
t.header = reversed
}
t.mutex.Unlock() t.mutex.Unlock()
t.reqBox.Set(reqHeader, nil) t.reqBox.Set(reqHeader, nil)
} }
@@ -280,17 +289,19 @@ func (t *Terminal) UpdateList(merger *Merger) {
t.reqBox.Set(reqList, nil) t.reqBox.Set(reqList, nil)
} }
func (t *Terminal) output() { func (t *Terminal) output() bool {
if t.printQuery { if t.printQuery {
fmt.Println(string(t.input)) fmt.Println(string(t.input))
} }
if len(t.expect) > 0 { if len(t.expect) > 0 {
fmt.Println(t.pressed) fmt.Println(t.pressed)
} }
if len(t.selected) == 0 { found := len(t.selected) > 0
if !found {
cnt := t.merger.Length() cnt := t.merger.Length()
if cnt > 0 && cnt > t.cy { if cnt > 0 && cnt > t.cy {
fmt.Println(t.merger.Get(t.cy).AsString(t.ansi)) fmt.Println(t.merger.Get(t.cy).AsString(t.ansi))
found = true
} }
} else { } else {
sels := make([]selectedItem, 0, len(t.selected)) sels := make([]selectedItem, 0, len(t.selected))
@@ -302,6 +313,7 @@ func (t *Terminal) output() {
fmt.Println(*sel.text) fmt.Println(*sel.text)
} }
} }
return found
} }
func runeWidth(r rune, prefixWidth int) int { func runeWidth(r rune, prefixWidth int) int {
@@ -433,9 +445,6 @@ func (t *Terminal) printHeader() {
max := t.maxHeight() max := t.maxHeight()
var state *ansiState var state *ansiState
for idx, lineStr := range t.header { for idx, lineStr := range t.header {
if !t.reverse {
idx = len(t.header) - idx - 1
}
line := idx + 2 line := idx + 2
if t.inlineInfo { if t.inlineInfo {
line-- line--
@@ -743,7 +752,7 @@ func (t *Terminal) Loop() {
} }
exit := func(code int) { exit := func(code int) {
if code == 0 && t.history != nil { if code <= exitNoMatch && t.history != nil {
t.history.append(string(t.input)) t.history.append(string(t.input))
} }
os.Exit(code) os.Exit(code)
@@ -776,11 +785,13 @@ func (t *Terminal) Loop() {
t.printAll() t.printAll()
case reqClose: case reqClose:
C.Close() C.Close()
t.output() if t.output() {
exit(0) exit(exitOk)
}
exit(exitNoMatch)
case reqQuit: case reqQuit:
C.Close() C.Close()
exit(1) exit(exitInterrupt)
} }
} }
t.placeCursor() t.placeCursor()

View File

@@ -737,8 +737,8 @@ class TestGoFZF < TestBase
assert_equal '6', readonce.chomp assert_equal '6', readonce.chomp
end end
def test_header_file def test_header
tmux.send_keys "seq 100 | #{fzf "--header-file <(head -5 #{__FILE__})"}", :Enter tmux.send_keys "seq 100 | #{fzf "--header \\\"\\$(head -5 #{__FILE__})\\\""}", :Enter
header = File.readlines(__FILE__).take(5).map(&:strip) header = File.readlines(__FILE__).take(5).map(&:strip)
tmux.until do |lines| tmux.until do |lines|
lines[-2].include?('100/100') && lines[-2].include?('100/100') &&
@@ -746,8 +746,8 @@ class TestGoFZF < TestBase
end end
end end
def test_header_file_reverse def test_header_reverse
tmux.send_keys "seq 100 | #{fzf "--header-file=<(head -5 #{__FILE__}) --reverse"}", :Enter tmux.send_keys "seq 100 | #{fzf "--header=\\\"\\$(head -5 #{__FILE__})\\\" --reverse"}", :Enter
header = File.readlines(__FILE__).take(5).map(&:strip) header = File.readlines(__FILE__).take(5).map(&:strip)
tmux.until do |lines| tmux.until do |lines|
lines[1].include?('100/100') && lines[1].include?('100/100') &&
@@ -755,6 +755,26 @@ class TestGoFZF < TestBase
end end
end end
def test_header_and_header_lines
tmux.send_keys "seq 100 | #{fzf "--header-lines 10 --header \\\"\\$(head -5 #{__FILE__})\\\""}", :Enter
header = File.readlines(__FILE__).take(5).map(&:strip)
tmux.until do |lines|
lines[-2].include?('90/90') &&
lines[-7...-2].map(&:strip) == header &&
lines[-17...-7].map(&:strip) == (1..10).map(&:to_s).reverse
end
end
def test_header_and_header_lines_reverse
tmux.send_keys "seq 100 | #{fzf "--reverse --header-lines 10 --header \\\"\\$(head -5 #{__FILE__})\\\""}", :Enter
header = File.readlines(__FILE__).take(5).map(&:strip)
tmux.until do |lines|
lines[1].include?('90/90') &&
lines[2...7].map(&:strip) == header &&
lines[7...17].map(&:strip) == (1..10).map(&:to_s)
end
end
def test_canel def test_canel
tmux.send_keys "seq 10 | #{fzf "--bind 2:cancel"}", :Enter tmux.send_keys "seq 10 | #{fzf "--bind 2:cancel"}", :Enter
tmux.until { |lines| lines[-2].include?('10/10') } tmux.until { |lines| lines[-2].include?('10/10') }
@@ -780,11 +800,6 @@ class TestGoFZF < TestBase
tmux.send_keys :Enter tmux.send_keys :Enter
end end
def test_invalid_term
tmux.send_keys "TERM=xxx fzf", :Enter
tmux.until { |lines| lines.any? { |l| l.include? 'Invalid $TERM: xxx' } }
end
def test_with_nth def test_with_nth
writelines tempname, ['hello world ', 'byebye'] writelines tempname, ['hello world ', 'byebye']
assert_equal 'hello world ', `cat #{tempname} | #{FZF} -f"^he hehe" -x -n 2.. --with-nth 2,1,1`.chomp assert_equal 'hello world ', `cat #{tempname} | #{FZF} -f"^he hehe" -x -n 2.. --with-nth 2,1,1`.chomp
@@ -801,6 +816,47 @@ class TestGoFZF < TestBase
assert_equal src, `cat #{tempname} | #{FZF} -fhehe -x -n 2.. --with-nth 2,1,1 --no-ansi`.chomp assert_equal src, `cat #{tempname} | #{FZF} -fhehe -x -n 2.. --with-nth 2,1,1 --no-ansi`.chomp
end end
def test_exit_0_exit_code
`echo foo | #{FZF} -q bar -0`
assert_equal 1, $?.exitstatus
end
def test_invalid_term
lines = `TERM=xxx #{FZF}`
assert_equal 2, $?.exitstatus
assert lines.include?('Invalid $TERM: xxx')
end
def test_invalid_option
lines = `#{FZF} --foobar 2>&1`
assert_equal 2, $?.exitstatus
assert lines.include?('unknown option: --foobar'), lines
end
def test_filter_exitstatus
# filter / streaming filter
["", "--no-sort"].each do |opts|
assert `echo foo | #{FZF} -f foo #{opts}`.include?('foo')
assert_equal 0, $?.exitstatus
assert `echo foo | #{FZF} -f bar #{opts}`.empty?
assert_equal 1, $?.exitstatus
end
end
def test_exitstatus_empty
{ '99' => '0', '999' => '1' }.each do |query, status|
tmux.send_keys "seq 100 | #{FZF} -q #{query}", :Enter
tmux.until { |lines| lines[-2] =~ %r{ [10]/100} }
tmux.send_keys :Enter
tmux.send_keys 'echo --\$?--'
tmux.until { |lines| lines.last.include? "echo --$?--" }
tmux.send_keys :Enter
tmux.until { |lines| lines.last.include? "--#{status}--" }
end
end
private private
def writelines path, lines def writelines path, lines
File.unlink path while File.exists? path File.unlink path while File.exists? path