Last active
February 16, 2016 23:53
-
-
Save junegunn/b047f0b98b794769592e to your computer and use it in GitHub Desktop.
WIP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/options.go b/src/options.go | |
index 962e516..6519d08 100644 | |
--- a/src/options.go | |
+++ b/src/options.go | |
@@ -91,42 +91,43 @@ func defaultMargin() [4]string { | |
// Options stores the values of command-line options | |
type Options struct { | |
- Fuzzy bool | |
- Extended bool | |
- Case Case | |
- Nth []Range | |
- WithNth []Range | |
- Delimiter Delimiter | |
- Sort int | |
- Tac bool | |
- Criteria []criterion | |
- Multi bool | |
- Ansi bool | |
- Mouse bool | |
- Theme *curses.ColorTheme | |
- Black bool | |
- Reverse bool | |
- Cycle bool | |
- Hscroll bool | |
- InlineInfo bool | |
- Prompt string | |
- Query string | |
- Select1 bool | |
- Exit0 bool | |
- Filter *string | |
- ToggleSort bool | |
- Expect map[int]string | |
- Keymap map[int]actionType | |
- Execmap map[int]string | |
- PrintQuery bool | |
- ReadZero bool | |
- Sync bool | |
- History *History | |
- Header []string | |
- HeaderLines int | |
- Margin [4]string | |
- Tabstop int | |
- Version bool | |
+ Fuzzy bool | |
+ Extended bool | |
+ Case Case | |
+ Nth []Range | |
+ WithNth []Range | |
+ Delimiter Delimiter | |
+ Sort int | |
+ Tac bool | |
+ Criteria []criterion | |
+ Multi bool | |
+ Ansi bool | |
+ Mouse bool | |
+ Theme *curses.ColorTheme | |
+ Black bool | |
+ Reverse bool | |
+ Cycle bool | |
+ Hscroll bool | |
+ InlineInfo bool | |
+ Prompt string | |
+ Query string | |
+ Select1 bool | |
+ Exit0 bool | |
+ Filter *string | |
+ ToggleSort bool | |
+ ToggleFollow bool | |
+ Expect map[int]string | |
+ Keymap map[int]actionType | |
+ Execmap map[int]string | |
+ PrintQuery bool | |
+ ReadZero bool | |
+ Sync bool | |
+ History *History | |
+ Header []string | |
+ HeaderLines int | |
+ Margin [4]string | |
+ Tabstop int | |
+ Version bool | |
} | |
func defaultTheme() *curses.ColorTheme { | |
@@ -138,42 +139,43 @@ func defaultTheme() *curses.ColorTheme { | |
func defaultOptions() *Options { | |
return &Options{ | |
- Fuzzy: true, | |
- Extended: true, | |
- Case: CaseSmart, | |
- Nth: make([]Range, 0), | |
- WithNth: make([]Range, 0), | |
- Delimiter: Delimiter{}, | |
- Sort: 1000, | |
- Tac: false, | |
- Criteria: []criterion{byMatchLen, byLength}, | |
- Multi: false, | |
- Ansi: false, | |
- Mouse: true, | |
- Theme: defaultTheme(), | |
- Black: false, | |
- Reverse: false, | |
- Cycle: false, | |
- Hscroll: true, | |
- InlineInfo: false, | |
- Prompt: "> ", | |
- Query: "", | |
- Select1: false, | |
- Exit0: false, | |
- Filter: nil, | |
- ToggleSort: false, | |
- Expect: make(map[int]string), | |
- Keymap: defaultKeymap(), | |
- Execmap: make(map[int]string), | |
- PrintQuery: false, | |
- ReadZero: false, | |
- Sync: false, | |
- History: nil, | |
- Header: make([]string, 0), | |
- HeaderLines: 0, | |
- Margin: defaultMargin(), | |
- Tabstop: 8, | |
- Version: false} | |
+ Fuzzy: true, | |
+ Extended: true, | |
+ Case: CaseSmart, | |
+ Nth: make([]Range, 0), | |
+ WithNth: make([]Range, 0), | |
+ Delimiter: Delimiter{}, | |
+ Sort: 1000, | |
+ Tac: false, | |
+ Criteria: []criterion{byMatchLen, byLength}, | |
+ Multi: false, | |
+ Ansi: false, | |
+ Mouse: true, | |
+ Theme: defaultTheme(), | |
+ Black: false, | |
+ Reverse: false, | |
+ Cycle: false, | |
+ Hscroll: true, | |
+ InlineInfo: false, | |
+ Prompt: "> ", | |
+ Query: "", | |
+ Select1: false, | |
+ Exit0: false, | |
+ Filter: nil, | |
+ ToggleSort: false, | |
+ ToggleFollow: false, | |
+ Expect: make(map[int]string), | |
+ Keymap: make(map[int]actionType), | |
+ Execmap: make(map[int]string), | |
+ PrintQuery: false, | |
+ ReadZero: false, | |
+ Sync: false, | |
+ History: nil, | |
+ Header: make([]string, 0), | |
+ HeaderLines: 0, | |
+ Margin: defaultMargin(), | |
+ Tabstop: 8, | |
+ Version: false} | |
} | |
func help(code int) { | |
@@ -484,7 +486,7 @@ const ( | |
escapedComma = 1 | |
) | |
-func parseKeymap(keymap map[int]actionType, execmap map[int]string, toggleSort bool, str string) (map[int]actionType, map[int]string, bool) { | |
+func parseKeymap(keymap map[int]actionType, execmap map[int]string, str string) { | |
if executeRegexp == nil { | |
// Backreferences are not supported. | |
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|') | |
@@ -590,9 +592,10 @@ func parseKeymap(keymap map[int]actionType, execmap map[int]string, toggleSort b | |
keymap[key] = actPreviousHistory | |
case "next-history": | |
keymap[key] = actNextHistory | |
+ case "toggle-follow": | |
+ keymap[key] = actToggleFollow | |
case "toggle-sort": | |
keymap[key] = actToggleSort | |
- toggleSort = true | |
default: | |
if isExecuteAction(actLower) { | |
var offset int | |
@@ -613,7 +616,6 @@ func parseKeymap(keymap map[int]actionType, execmap map[int]string, toggleSort b | |
} | |
} | |
} | |
- return keymap, execmap, toggleSort | |
} | |
func isExecuteAction(str string) bool { | |
@@ -635,13 +637,12 @@ func isExecuteAction(str string) bool { | |
return false | |
} | |
-func checkToggleSort(keymap map[int]actionType, str string) map[int]actionType { | |
+func parseToggleSort(keymap map[int]actionType, str string) { | |
keys := parseKeyChords(str, "key name required") | |
if len(keys) != 1 { | |
errorExit("multiple keys specified") | |
} | |
keymap[firstKey(keys)] = actToggleSort | |
- return keymap | |
} | |
func strLines(str string) []string { | |
@@ -691,7 +692,6 @@ func parseMargin(margin string) [4]string { | |
} | |
func parseOptions(opts *Options, allArgs []string) { | |
- keymap := make(map[int]actionType) | |
var historyMax int | |
if opts.History == nil { | |
historyMax = defaultHistoryMax | |
@@ -741,8 +741,7 @@ func parseOptions(opts *Options, allArgs []string) { | |
case "--tiebreak": | |
opts.Criteria = parseTiebreak(nextString(allArgs, &i, "sort criterion required")) | |
case "--bind": | |
- keymap, opts.Execmap, opts.ToggleSort = | |
- parseKeymap(keymap, opts.Execmap, opts.ToggleSort, nextString(allArgs, &i, "bind expression required")) | |
+ parseKeymap(opts.Keymap, opts.Execmap, nextString(allArgs, &i, "bind expression required")) | |
case "--color": | |
spec := optionalNextString(allArgs, &i) | |
if len(spec) == 0 { | |
@@ -751,7 +750,7 @@ func parseOptions(opts *Options, allArgs []string) { | |
opts.Theme = parseTheme(opts.Theme, spec) | |
} | |
case "--toggle-sort": | |
- keymap = checkToggleSort(keymap, nextString(allArgs, &i, "key name required")) | |
+ parseToggleSort(opts.Keymap, nextString(allArgs, &i, "key name required")) | |
opts.ToggleSort = true | |
case "-d", "--delimiter": | |
opts.Delimiter = delimiterRegexp(nextString(allArgs, &i, "delimiter required")) | |
@@ -869,7 +868,7 @@ func parseOptions(opts *Options, allArgs []string) { | |
} else if match, _ := optString(arg, "-s", "--sort="); match { | |
opts.Sort = 1 // Don't care | |
} else if match, value := optString(arg, "--toggle-sort="); match { | |
- keymap = checkToggleSort(keymap, value) | |
+ parseToggleSort(opts.Keymap, value) | |
opts.ToggleSort = true | |
} else if match, value := optString(arg, "--expect="); match { | |
opts.Expect = parseKeyChords(value, "key names required") | |
@@ -878,8 +877,7 @@ func parseOptions(opts *Options, allArgs []string) { | |
} else if match, value := optString(arg, "--color="); match { | |
opts.Theme = parseTheme(opts.Theme, value) | |
} else if match, value := optString(arg, "--bind="); match { | |
- keymap, opts.Execmap, opts.ToggleSort = | |
- parseKeymap(keymap, opts.Execmap, opts.ToggleSort, value) | |
+ parseKeymap(opts.Keymap, opts.Execmap, value) | |
} else if match, value := optString(arg, "--history="); match { | |
setHistory(value) | |
} else if match, value := optString(arg, "--history-size="); match { | |
@@ -905,21 +903,32 @@ func parseOptions(opts *Options, allArgs []string) { | |
if opts.Tabstop < 1 { | |
errorExit("tab stop must be a positive integer") | |
} | |
+} | |
- // Change default actions for CTRL-N / CTRL-P when --history is used | |
+func postProcessOptions(opts *Options) *Options { | |
+ // Default actions for CTRL-N / CTRL-P when --history is set | |
if opts.History != nil { | |
- if _, prs := keymap[curses.CtrlP]; !prs { | |
- keymap[curses.CtrlP] = actPreviousHistory | |
+ if _, prs := opts.Keymap[curses.CtrlP]; !prs { | |
+ opts.Keymap[curses.CtrlP] = actPreviousHistory | |
} | |
- if _, prs := keymap[curses.CtrlN]; !prs { | |
- keymap[curses.CtrlN] = actNextHistory | |
+ if _, prs := opts.Keymap[curses.CtrlN]; !prs { | |
+ opts.Keymap[curses.CtrlN] = actNextHistory | |
} | |
} | |
- // Override default key bindings | |
- for key, act := range keymap { | |
- opts.Keymap[key] = act | |
+ // Extend the default key map | |
+ keymap := defaultKeymap() | |
+ for key, act := range opts.Keymap { | |
+ // Check if actToggleSort or actToggleFollow is set | |
+ switch act { | |
+ case actToggleSort: | |
+ opts.ToggleSort = true | |
+ case actToggleFollow: | |
+ opts.ToggleFollow = true | |
+ } | |
+ keymap[key] = act | |
} | |
+ opts.Keymap = keymap | |
// If we're not using extended search mode, --nth option becomes irrelevant | |
// if it contains the whole range | |
@@ -927,10 +936,11 @@ func parseOptions(opts *Options, allArgs []string) { | |
for _, r := range opts.Nth { | |
if r.begin == rangeEllipsis && r.end == rangeEllipsis { | |
opts.Nth = make([]Range, 0) | |
- return | |
+ break | |
} | |
} | |
} | |
+ return opts | |
} | |
// ParseOptions parses command-line options | |
@@ -939,9 +949,12 @@ func ParseOptions() *Options { | |
// Options from Env var | |
words, _ := shellwords.Parse(os.Getenv("FZF_DEFAULT_OPTS")) | |
- parseOptions(opts, words) | |
+ if len(words) > 0 { | |
+ parseOptions(opts, words) | |
+ } | |
// Options from command-line arguments | |
parseOptions(opts, os.Args[1:]) | |
- return opts | |
+ | |
+ return postProcessOptions(opts) | |
} | |
diff --git a/src/options_test.go b/src/options_test.go | |
index ef86abe..fd7510e 100644 | |
--- a/src/options_test.go | |
+++ b/src/options_test.go | |
@@ -96,6 +96,7 @@ func TestIrrelevantNth(t *testing.T) { | |
opts := defaultOptions() | |
words := []string{"--nth", "..", "-x"} | |
parseOptions(opts, words) | |
+ postProcessOptions(opts) | |
if len(opts.Nth) != 0 { | |
t.Errorf("nth should be empty: %s", opts.Nth) | |
} | |
@@ -104,6 +105,7 @@ func TestIrrelevantNth(t *testing.T) { | |
{ | |
opts := defaultOptions() | |
parseOptions(opts, words) | |
+ postProcessOptions(opts) | |
if len(opts.Nth) != 0 { | |
t.Errorf("nth should be empty: %s", opts.Nth) | |
} | |
@@ -112,6 +114,7 @@ func TestIrrelevantNth(t *testing.T) { | |
opts := defaultOptions() | |
words = append(words, "-x") | |
parseOptions(opts, words) | |
+ postProcessOptions(opts) | |
if len(opts.Nth) != 2 { | |
t.Errorf("nth should not be empty: %s", opts.Nth) | |
} | |
@@ -231,17 +234,14 @@ func TestBind(t *testing.T) { | |
keymap := defaultKeymap() | |
execmap := make(map[int]string) | |
check(actBeginningOfLine, keymap[curses.CtrlA]) | |
- keymap, execmap, toggleSort := | |
- parseKeymap(keymap, execmap, false, | |
- "ctrl-a:kill-line,ctrl-b:toggle-sort,c:page-up,alt-z:page-down,"+ | |
- "f1:execute(ls {}),f2:execute/echo {}, {}, {}/,f3:execute[echo '({})'],f4:execute;less {};,"+ | |
- "alt-a:execute@echo (,),[,],/,:,;,%,{}@,alt-b:execute;echo (,),[,],/,:,@,%,{};"+ | |
- ",,:abort,::accept,X:execute:\nfoobar,Y:execute(baz)") | |
- if !toggleSort { | |
- t.Errorf("toggleSort not set") | |
- } | |
+ parseKeymap(keymap, execmap, | |
+ "ctrl-a:kill-line,ctrl-b:toggle-sort,ctrl-c:toggle-follow,c:page-up,alt-z:page-down,"+ | |
+ "f1:execute(ls {}),f2:execute/echo {}, {}, {}/,f3:execute[echo '({})'],f4:execute;less {};,"+ | |
+ "alt-a:execute@echo (,),[,],/,:,;,%,{}@,alt-b:execute;echo (,),[,],/,:,@,%,{};"+ | |
+ ",,:abort,::accept,X:execute:\nfoobar,Y:execute(baz)") | |
check(actKillLine, keymap[curses.CtrlA]) | |
check(actToggleSort, keymap[curses.CtrlB]) | |
+ check(actToggleFollow, keymap[curses.CtrlC]) | |
check(actPageUp, keymap[curses.AltZ+'c']) | |
check(actAbort, keymap[curses.AltZ+',']) | |
check(actAccept, keymap[curses.AltZ+':']) | |
@@ -259,15 +259,11 @@ func TestBind(t *testing.T) { | |
checkString("\nfoobar,Y:execute(baz)", execmap[curses.AltZ+'X']) | |
for idx, char := range []rune{'~', '!', '@', '#', '$', '%', '^', '&', '*', '|', ';', '/'} { | |
- keymap, execmap, toggleSort = | |
- parseKeymap(keymap, execmap, false, fmt.Sprintf("%d:execute%cfoobar%c", idx%10, char, char)) | |
+ parseKeymap(keymap, execmap, fmt.Sprintf("%d:execute%cfoobar%c", idx%10, char, char)) | |
checkString("foobar", execmap[curses.AltZ+int([]rune(fmt.Sprintf("%d", idx%10))[0])]) | |
} | |
- keymap, execmap, toggleSort = parseKeymap(keymap, execmap, false, "f1:abort") | |
- if toggleSort { | |
- t.Errorf("toggleSort set") | |
- } | |
+ parseKeymap(keymap, execmap, "f1:abort") | |
check(actAbort, keymap[curses.F1]) | |
} | |
@@ -328,3 +324,63 @@ func TestParseNilTheme(t *testing.T) { | |
t.Errorf("color should now be enabled and customized") | |
} | |
} | |
+ | |
+func TestDefaultCtrlNP(t *testing.T) { | |
+ check := func(words []string, key int, expected actionType) { | |
+ opts := defaultOptions() | |
+ parseOptions(opts, words) | |
+ postProcessOptions(opts) | |
+ if opts.Keymap[key] != expected { | |
+ t.Error() | |
+ } | |
+ } | |
+ check([]string{}, curses.CtrlN, actDown) | |
+ check([]string{}, curses.CtrlP, actUp) | |
+ | |
+ check([]string{"--bind=ctrl-n:accept"}, curses.CtrlN, actAccept) | |
+ check([]string{"--bind=ctrl-p:accept"}, curses.CtrlP, actAccept) | |
+ | |
+ hist := "--history=/tmp/foo" | |
+ check([]string{hist}, curses.CtrlN, actNextHistory) | |
+ check([]string{hist}, curses.CtrlP, actPreviousHistory) | |
+ | |
+ check([]string{hist, "--bind=ctrl-n:accept"}, curses.CtrlN, actAccept) | |
+ check([]string{hist, "--bind=ctrl-n:accept"}, curses.CtrlP, actPreviousHistory) | |
+ | |
+ check([]string{hist, "--bind=ctrl-p:accept"}, curses.CtrlN, actNextHistory) | |
+ check([]string{hist, "--bind=ctrl-p:accept"}, curses.CtrlP, actAccept) | |
+} | |
+ | |
+func TestToggle(t *testing.T) { | |
+ optsFor := func(words ...string) *Options { | |
+ opts := defaultOptions() | |
+ parseOptions(opts, words) | |
+ postProcessOptions(opts) | |
+ return opts | |
+ } | |
+ | |
+ opts := optsFor() | |
+ if opts.ToggleSort || opts.ToggleFollow { | |
+ t.Error() | |
+ } | |
+ | |
+ opts = optsFor("--bind=a:toggle-sort") | |
+ if !opts.ToggleSort || opts.ToggleFollow { | |
+ t.Error() | |
+ } | |
+ | |
+ opts = optsFor("--bind=a:toggle-follow") | |
+ if opts.ToggleSort || !opts.ToggleFollow { | |
+ t.Error() | |
+ } | |
+ | |
+ opts = optsFor("--bind=a:toggle-sort,b:toggle-follow") | |
+ if !opts.ToggleSort || !opts.ToggleFollow { | |
+ t.Error() | |
+ } | |
+ | |
+ opts = optsFor("--bind=a:toggle-sort,b:toggle-follow", "--bind=a:up,b:down") | |
+ if opts.ToggleSort || opts.ToggleFollow { | |
+ t.Error() | |
+ } | |
+} | |
diff --git a/src/terminal.go b/src/terminal.go | |
index f1ddc48..430bb19 100644 | |
--- a/src/terminal.go | |
+++ b/src/terminal.go | |
@@ -21,42 +21,44 @@ import ( | |
// Terminal represents terminal input/output | |
type Terminal struct { | |
- initDelay time.Duration | |
- inlineInfo bool | |
- prompt string | |
- reverse bool | |
- hscroll bool | |
- cx int | |
- cy int | |
- offset int | |
- yanked []rune | |
- input []rune | |
- multi bool | |
- sort bool | |
- toggleSort bool | |
- expect map[int]string | |
- keymap map[int]actionType | |
- execmap map[int]string | |
- pressed string | |
- printQuery bool | |
- history *History | |
- cycle bool | |
- header []string | |
- header0 []string | |
- ansi bool | |
- margin [4]string | |
- marginInt [4]int | |
- count int | |
- progress int | |
- reading bool | |
- merger *Merger | |
- selected map[int32]selectedItem | |
- reqBox *util.EventBox | |
- eventBox *util.EventBox | |
- mutex sync.Mutex | |
- initFunc func() | |
- suppress bool | |
- startChan chan bool | |
+ initDelay time.Duration | |
+ inlineInfo bool | |
+ prompt string | |
+ reverse bool | |
+ hscroll bool | |
+ cx int | |
+ cy int | |
+ offset int | |
+ yanked []rune | |
+ input []rune | |
+ multi bool | |
+ sort bool | |
+ follow bool | |
+ toggleSort bool | |
+ toggleFollow bool | |
+ expect map[int]string | |
+ keymap map[int]actionType | |
+ execmap map[int]string | |
+ pressed string | |
+ printQuery bool | |
+ history *History | |
+ cycle bool | |
+ header []string | |
+ header0 []string | |
+ ansi bool | |
+ margin [4]string | |
+ marginInt [4]int | |
+ count int | |
+ progress int | |
+ reading bool | |
+ merger *Merger | |
+ selected map[int32]selectedItem | |
+ reqBox *util.EventBox | |
+ eventBox *util.EventBox | |
+ mutex sync.Mutex | |
+ initFunc func() | |
+ suppress bool | |
+ startChan chan bool | |
} | |
type selectedItem struct { | |
@@ -131,6 +133,7 @@ const ( | |
actUp | |
actPageUp | |
actPageDown | |
+ actToggleFollow | |
actToggleSort | |
actPreviousHistory | |
actNextHistory | |
@@ -205,39 +208,41 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { | |
delay = initialDelay | |
} | |
return &Terminal{ | |
- initDelay: delay, | |
- inlineInfo: opts.InlineInfo, | |
- prompt: opts.Prompt, | |
- reverse: opts.Reverse, | |
- hscroll: opts.Hscroll, | |
- cx: len(input), | |
- cy: 0, | |
- offset: 0, | |
- yanked: []rune{}, | |
- input: input, | |
- multi: opts.Multi, | |
- sort: opts.Sort > 0, | |
- toggleSort: opts.ToggleSort, | |
- expect: opts.Expect, | |
- keymap: opts.Keymap, | |
- execmap: opts.Execmap, | |
- pressed: "", | |
- printQuery: opts.PrintQuery, | |
- history: opts.History, | |
- margin: opts.Margin, | |
- marginInt: [4]int{0, 0, 0, 0}, | |
- cycle: opts.Cycle, | |
- header: header, | |
- header0: header, | |
- ansi: opts.Ansi, | |
- reading: true, | |
- merger: EmptyMerger, | |
- selected: make(map[int32]selectedItem), | |
- reqBox: util.NewEventBox(), | |
- eventBox: eventBox, | |
- mutex: sync.Mutex{}, | |
- suppress: true, | |
- startChan: make(chan bool, 1), | |
+ initDelay: delay, | |
+ inlineInfo: opts.InlineInfo, | |
+ prompt: opts.Prompt, | |
+ reverse: opts.Reverse, | |
+ hscroll: opts.Hscroll, | |
+ cx: len(input), | |
+ cy: 0, | |
+ offset: 0, | |
+ yanked: []rune{}, | |
+ input: input, | |
+ multi: opts.Multi, | |
+ sort: opts.Sort > 0, | |
+ toggleSort: opts.ToggleSort, | |
+ follow: true, | |
+ toggleFollow: opts.Tac && opts.ToggleFollow, | |
+ expect: opts.Expect, | |
+ keymap: opts.Keymap, | |
+ execmap: opts.Execmap, | |
+ pressed: "", | |
+ printQuery: opts.PrintQuery, | |
+ history: opts.History, | |
+ margin: opts.Margin, | |
+ marginInt: [4]int{0, 0, 0, 0}, | |
+ cycle: opts.Cycle, | |
+ header: header, | |
+ header0: header, | |
+ ansi: opts.Ansi, | |
+ reading: true, | |
+ merger: EmptyMerger, | |
+ selected: make(map[int32]selectedItem), | |
+ reqBox: util.NewEventBox(), | |
+ eventBox: eventBox, | |
+ mutex: sync.Mutex{}, | |
+ suppress: true, | |
+ startChan: make(chan bool, 1), | |
initFunc: func() { | |
C.Init(opts.Theme, opts.Black, opts.Mouse) | |
}} | |
@@ -296,6 +301,11 @@ func (t *Terminal) UpdateProgress(progress float32) { | |
func (t *Terminal) UpdateList(merger *Merger) { | |
t.mutex.Lock() | |
t.progress = 100 | |
+ if t.toggleFollow && !t.follow { | |
+ diff := merger.Length() - t.merger.Length() | |
+ t.cy += diff | |
+ t.offset += diff | |
+ } | |
t.merger = merger | |
t.mutex.Unlock() | |
t.reqBox.Set(reqInfo, nil) | |
@@ -435,12 +445,11 @@ func (t *Terminal) printInfo() { | |
} | |
output := fmt.Sprintf("%d/%d", t.merger.Length(), t.count) | |
- if t.toggleSort { | |
- if t.sort { | |
- output += "/S" | |
- } else { | |
- output += " " | |
- } | |
+ if t.toggleSort && t.sort { | |
+ output += "/S" | |
+ } | |
+ if t.toggleFollow && t.reading && t.follow { | |
+ output += "/F" | |
} | |
if t.multi && len(t.selected) > 0 { | |
output += fmt.Sprintf(" (%d)", len(t.selected)) | |
@@ -892,6 +901,8 @@ func (t *Terminal) Loop() { | |
case actInvalid: | |
t.mutex.Unlock() | |
return false | |
+ case actToggleFollow: | |
+ t.follow = !t.follow | |
case actToggleSort: | |
t.sort = !t.sort | |
t.eventBox.Set(EvtSearchNew, t.sort) | |
@@ -1117,25 +1128,8 @@ func (t *Terminal) Loop() { | |
} | |
func (t *Terminal) constrain() { | |
- count := t.merger.Length() | |
- height := t.maxItems() | |
- diffpos := t.cy - t.offset | |
- | |
- t.cy = util.Constrain(t.cy, 0, count-1) | |
- | |
- if t.cy > t.offset+(height-1) { | |
- // Ceil | |
- t.offset = t.cy - (height - 1) | |
- } else if t.offset > t.cy { | |
- // Floor | |
- t.offset = t.cy | |
- } | |
- | |
- // Adjustment | |
- if count-t.offset < height { | |
- t.offset = util.Max(0, count-height) | |
- t.cy = util.Constrain(t.offset+diffpos, 0, count-1) | |
- } | |
+ t.cy = util.Constrain(t.cy, 0, t.merger.Length()-1) | |
+ t.offset = util.Constrain(t.offset, t.cy-t.maxItems()+1, t.cy) | |
t.offset = util.Max(0, t.offset) | |
} | |
diff --git a/test/test_go.rb b/test/test_go.rb | |
index 7a2d003..48e2970 100644 | |
--- a/test/test_go.rb | |
+++ b/test/test_go.rb | |
@@ -450,7 +450,7 @@ class TestGoFZF < TestBase | |
tmux.send_keys "seq 1 111 | #{fzf "-m +s --tac #{opt} -q11"}", :Enter | |
tmux.until { |lines| lines[-3].include? '> 111' } | |
tmux.send_keys :Tab | |
- tmux.until { |lines| lines[-2].include? '4/111 (1)' } | |
+ tmux.until { |lines| lines[-2].include? '4/111 (1)' } | |
tmux.send_keys 'C-R' | |
tmux.until { |lines| lines[-3].include? '> 11' } | |
tmux.send_keys :Tab | |
@@ -1137,6 +1137,19 @@ class TestGoFZF < TestBase | |
`seq 10 | #{FZF} -f '1 | !1'`.lines.map(&:chomp) | |
end | |
+ def test_toggle_follow | |
+ tmux.send_keys "(seq 1000; sleep 0.5; seq 1001 2000; sleep 0.5; seq 2001 3000) | #{fzf '--bind space:toggle-follow --tac'}", :Enter | |
+ tmux.until { |lines| lines[-2].end_with? '1000/1000/F' } | |
+ tmux.send_keys :Space | |
+ tmux.until { |lines| lines[-2].end_with? '1000/1000' } | |
+ tmux.until { |lines| lines[-2].end_with? '2000/2000' } | |
+ tmux.send_keys :Space | |
+ tmux.until { |lines| lines[-2].end_with? '2000/2000/F' } | |
+ tmux.until { |lines| lines[-2].end_with? '3000/3000' } | |
+ tmux.send_keys :Down, :Enter | |
+ assert_equal '2001', readonce.chomp | |
+ end | |
+ | |
private | |
def writelines path, lines | |
File.unlink path while File.exists? path |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment