Change exit status (0: OK, 1: No match, 2: Error/Interrupted)

A la grep. Close #345
This commit is contained in:
Junegunn Choi 2015-09-15 13:21:51 +09:00
parent fa2f9f1f21
commit 65d9d416b4
6 changed files with 76 additions and 20 deletions

View File

@ -47,3 +47,9 @@ const (
EvtHeader EvtHeader
EvtClose EvtClose
) )
const (
exitOk = 0
exitNoMatch = 1
exitError = 2
)

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
@ -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

@ -261,7 +261,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 +275,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

@ -180,14 +180,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) {
@ -682,7 +682,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":

View File

@ -280,17 +280,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 +304,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 {
@ -743,7 +746,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 +779,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(exitError)
} }
} }
t.placeCursor() t.placeCursor()

View File

@ -780,11 +780,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 +796,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