Skip to content

Instantly share code, notes, and snippets.

@junegunn
Last active February 16, 2016 23:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save junegunn/b047f0b98b794769592e to your computer and use it in GitHub Desktop.
Save junegunn/b047f0b98b794769592e to your computer and use it in GitHub Desktop.
WIP
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