diff options
author | Junegunn Choi <junegunn.c@gmail.com> | 2015-02-26 01:42:15 +0900 |
---|---|---|
committer | Junegunn Choi <junegunn.c@gmail.com> | 2015-02-26 01:42:15 +0900 |
commit | c1aa5c5f3380315621d30d99b258667775b0fad3 (patch) | |
tree | 5bfbff2ba5ad7ce1cb8d914106a91bf2cc2939e7 /src | |
parent | 4a1752d3fc7f069b0f8afb12ed625acb6fd2aee2 (diff) |
Add --tac option and reverse display order of --no-sort
DISCLAIMER: This is a backward incompatible change
Diffstat (limited to 'src')
-rw-r--r-- | src/constants.go | 2 | ||||
-rw-r--r-- | src/core.go | 2 | ||||
-rw-r--r-- | src/item.go | 27 | ||||
-rw-r--r-- | src/item_test.go | 15 | ||||
-rw-r--r-- | src/matcher.go | 12 | ||||
-rw-r--r-- | src/merger.go | 32 | ||||
-rw-r--r-- | src/merger_test.go | 6 | ||||
-rw-r--r-- | src/options.go | 13 | ||||
-rw-r--r-- | src/terminal.go | 20 |
9 files changed, 79 insertions, 50 deletions
diff --git a/src/constants.go b/src/constants.go index 7d542234..f5138534 100644 --- a/src/constants.go +++ b/src/constants.go @@ -5,7 +5,7 @@ import ( ) // Current version -const Version = "0.9.3" +const Version = "0.9.4" // fzf events const ( diff --git a/src/core.go b/src/core.go index ea97b4e6..ec4c5e8d 100644 --- a/src/core.go +++ b/src/core.go @@ -93,7 +93,7 @@ func Run(options *Options) { return BuildPattern( opts.Mode, opts.Case, opts.Nth, opts.Delimiter, runes) } - matcher := NewMatcher(patternBuilder, opts.Sort > 0, eventBox) + matcher := NewMatcher(patternBuilder, opts.Sort > 0, opts.Tac, eventBox) // Filtering mode if opts.Filter != nil { diff --git a/src/item.go b/src/item.go index 4cbd3f98..2b8a9d13 100644 --- a/src/item.go +++ b/src/item.go @@ -87,10 +87,28 @@ func (a ByRelevance) Less(i, j int) bool { irank := a[i].Rank(true) jrank := a[j].Rank(true) - return compareRanks(irank, jrank) + return compareRanks(irank, jrank, false) } -func compareRanks(irank Rank, jrank Rank) bool { +// ByRelevanceTac is for sorting Items +type ByRelevanceTac []*Item + +func (a ByRelevanceTac) Len() int { + return len(a) +} + +func (a ByRelevanceTac) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func (a ByRelevanceTac) Less(i, j int) bool { + irank := a[i].Rank(true) + jrank := a[j].Rank(true) + + return compareRanks(irank, jrank, true) +} + +func compareRanks(irank Rank, jrank Rank, tac bool) bool { if irank.matchlen < jrank.matchlen { return true } else if irank.matchlen > jrank.matchlen { @@ -103,8 +121,5 @@ func compareRanks(irank Rank, jrank Rank) bool { return false } - if irank.index <= jrank.index { - return true - } - return false + return (irank.index <= jrank.index) != tac } diff --git a/src/item_test.go b/src/item_test.go index 0e83631a..372ab4ae 100644 --- a/src/item_test.go +++ b/src/item_test.go @@ -20,12 +20,19 @@ func TestOffsetSort(t *testing.T) { } func TestRankComparison(t *testing.T) { - if compareRanks(Rank{3, 0, 5}, Rank{2, 0, 7}) || - !compareRanks(Rank{3, 0, 5}, Rank{3, 0, 6}) || - !compareRanks(Rank{1, 2, 3}, Rank{1, 3, 2}) || - !compareRanks(Rank{0, 0, 0}, Rank{0, 0, 0}) { + if compareRanks(Rank{3, 0, 5}, Rank{2, 0, 7}, false) || + !compareRanks(Rank{3, 0, 5}, Rank{3, 0, 6}, false) || + !compareRanks(Rank{1, 2, 3}, Rank{1, 3, 2}, false) || + !compareRanks(Rank{0, 0, 0}, Rank{0, 0, 0}, false) { t.Error("Invalid order") } + + if compareRanks(Rank{3, 0, 5}, Rank{2, 0, 7}, true) || + !compareRanks(Rank{3, 0, 5}, Rank{3, 0, 6}, false) || + !compareRanks(Rank{1, 2, 3}, Rank{1, 3, 2}, true) || + !compareRanks(Rank{0, 0, 0}, Rank{0, 0, 0}, false) { + t.Error("Invalid order (tac)") + } } // Match length, string length, index diff --git a/src/matcher.go b/src/matcher.go index bfe9d287..0879a088 100644 --- a/src/matcher.go +++ b/src/matcher.go @@ -21,6 +21,7 @@ type MatchRequest struct { type Matcher struct { patternBuilder func([]rune) *Pattern sort bool + tac bool eventBox *util.EventBox reqBox *util.EventBox partitions int @@ -38,10 +39,11 @@ const ( // NewMatcher returns a new Matcher func NewMatcher(patternBuilder func([]rune) *Pattern, - sort bool, eventBox *util.EventBox) *Matcher { + sort bool, tac bool, eventBox *util.EventBox) *Matcher { return &Matcher{ patternBuilder: patternBuilder, sort: sort, + tac: tac, eventBox: eventBox, reqBox: util.NewEventBox(), partitions: runtime.NumCPU(), @@ -159,7 +161,11 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) { countChan <- len(matches) } if !empty && m.sort { - sort.Sort(ByRelevance(sliceMatches)) + if m.tac { + sort.Sort(ByRelevanceTac(sliceMatches)) + } else { + sort.Sort(ByRelevance(sliceMatches)) + } } resultChan <- partialResult{idx, sliceMatches} }(idx, chunks) @@ -195,7 +201,7 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) { partialResult := <-resultChan partialResults[partialResult.index] = partialResult.matches } - return NewMerger(partialResults, !empty && m.sort), false + return NewMerger(partialResults, !empty && m.sort, m.tac), false } // Reset is called to interrupt/signal the ongoing search diff --git a/src/merger.go b/src/merger.go index 5bfc81d5..41323c18 100644 --- a/src/merger.go +++ b/src/merger.go @@ -3,7 +3,7 @@ package fzf import "fmt" // Merger with no data -var EmptyMerger = NewMerger([][]*Item{}, false) +var EmptyMerger = NewMerger([][]*Item{}, false, false) // Merger holds a set of locally sorted lists of items and provides the view of // a single, globally-sorted list @@ -12,17 +12,19 @@ type Merger struct { merged []*Item cursors []int sorted bool + tac bool final bool count int } // NewMerger returns a new Merger -func NewMerger(lists [][]*Item, sorted bool) *Merger { +func NewMerger(lists [][]*Item, sorted bool, tac bool) *Merger { mg := Merger{ lists: lists, merged: []*Item{}, cursors: make([]int, len(lists)), sorted: sorted, + tac: tac, final: false, count: 0} @@ -39,19 +41,21 @@ func (mg *Merger) Length() int { // Get returns the pointer to the Item object indexed by the given integer func (mg *Merger) Get(idx int) *Item { - if len(mg.lists) == 1 { - return mg.lists[0][idx] - } else if !mg.sorted { - for _, list := range mg.lists { - numItems := len(list) - if idx < numItems { - return list[idx] - } - idx -= numItems + if mg.sorted { + return mg.mergedGet(idx) + } + + if mg.tac { + idx = mg.Length() - idx - 1 + } + for _, list := range mg.lists { + numItems := len(list) + if idx < numItems { + return list[idx] } - panic(fmt.Sprintf("Index out of bounds (unsorted, %d/%d)", idx, mg.count)) + idx -= numItems } - return mg.mergedGet(idx) + panic(fmt.Sprintf("Index out of bounds (unsorted, %d/%d)", idx, mg.count)) } func (mg *Merger) mergedGet(idx int) *Item { @@ -66,7 +70,7 @@ func (mg *Merger) mergedGet(idx int) *Item { } if cursor >= 0 { rank := list[cursor].Rank(false) - if minIdx < 0 || compareRanks(rank, minRank) { + if minIdx < 0 || compareRanks(rank, minRank, mg.tac) { minRank = rank minIdx = listIdx } diff --git a/src/merger_test.go b/src/merger_test.go index f79da09a..b69d6338 100644 --- a/src/merger_test.go +++ b/src/merger_test.go @@ -62,7 +62,7 @@ func TestMergerUnsorted(t *testing.T) { cnt := len(items) // Not sorted: same order - mg := NewMerger(lists, false) + mg := NewMerger(lists, false, false) assert(t, cnt == mg.Length(), "Invalid Length") for i := 0; i < cnt; i++ { assert(t, items[i] == mg.Get(i), "Invalid Get") @@ -74,7 +74,7 @@ func TestMergerSorted(t *testing.T) { cnt := len(items) // Sorted sorted order - mg := NewMerger(lists, true) + mg := NewMerger(lists, true, false) assert(t, cnt == mg.Length(), "Invalid Length") sort.Sort(ByRelevance(items)) for i := 0; i < cnt; i++ { @@ -84,7 +84,7 @@ func TestMergerSorted(t *testing.T) { } // Inverse order - mg2 := NewMerger(lists, true) + mg2 := NewMerger(lists, true, false) for i := cnt - 1; i >= 0; i-- { if items[i] != mg2.Get(i) { t.Error("Not sorted", items[i], mg2.Get(i)) diff --git a/src/options.go b/src/options.go index c426e777..dc8f0b84 100644 --- a/src/options.go +++ b/src/options.go @@ -11,7 +11,7 @@ import ( const usage = `usage: fzf [options] - Search + Search mode -x, --extended Extended-search mode -e, --extended-exact Extended-search mode (exact match) -i Case-insensitive match (default: smart-case match) @@ -23,8 +23,9 @@ const usage = `usage: fzf [options] -d, --delimiter=STR Field delimiter regex for --nth (default: AWK-style) Search result - -s, --sort Sort the result - +s, --no-sort Do not sort the result. Keep the sequence unchanged. + +s, --no-sort Do not sort the result + --tac Reverse the order of the input + (e.g. 'history | fzf --tac --no-sort') Interface -m, --multi Enable multi-select with tab/shift-tab @@ -78,6 +79,7 @@ type Options struct { WithNth []Range Delimiter *regexp.Regexp Sort int + Tac bool Multi bool Mouse bool Color bool @@ -102,6 +104,7 @@ func defaultOptions() *Options { WithNth: make([]Range, 0), Delimiter: nil, Sort: 1000, + Tac: false, Multi: false, Mouse: true, Color: true, @@ -212,6 +215,10 @@ func parseOptions(opts *Options, allArgs []string) { opts.Sort = optionalNumeric(allArgs, &i) case "+s", "--no-sort": opts.Sort = 0 + case "--tac": + opts.Tac = true + case "--no-tac": + opts.Tac = false case "-i": opts.Case = CaseIgnore case "+i": diff --git a/src/terminal.go b/src/terminal.go index 3d914ac5..bd426d1a 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -22,7 +22,6 @@ import ( type Terminal struct { prompt string reverse bool - tac bool cx int cy int offset int @@ -85,7 +84,6 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { input := []rune(opts.Query) return &Terminal{ prompt: opts.Prompt, - tac: opts.Sort == 0, reverse: opts.Reverse, cx: len(input), cy: 0, @@ -148,13 +146,6 @@ func (t *Terminal) UpdateList(merger *Merger) { t.reqBox.Set(reqList, nil) } -func (t *Terminal) listIndex(y int) int { - if t.tac { - return t.merger.Length() - y - 1 - } - return y -} - func (t *Terminal) output() { if t.printQuery { fmt.Println(string(t.input)) @@ -162,7 +153,7 @@ func (t *Terminal) output() { if len(t.selected) == 0 { cnt := t.merger.Length() if cnt > 0 && cnt > t.cy { - fmt.Println(t.merger.Get(t.listIndex(t.cy)).AsString()) + fmt.Println(t.merger.Get(t.cy).AsString()) } } else { sels := make([]selectedItem, 0, len(t.selected)) @@ -246,7 +237,7 @@ func (t *Terminal) printList() { for i := 0; i < maxy; i++ { t.move(i+2, 0, true) if i < count { - t.printItem(t.merger.Get(t.listIndex(i+t.offset)), i == t.cy-t.offset) + t.printItem(t.merger.Get(i+t.offset), i == t.cy-t.offset) } } } @@ -525,9 +516,8 @@ func (t *Terminal) Loop() { } } toggle := func() { - idx := t.listIndex(t.cy) - if idx < t.merger.Length() { - item := t.merger.Get(idx) + if t.cy < t.merger.Length() { + item := t.merger.Get(t.cy) if _, found := t.selected[item.text]; !found { var strptr *string if item.origText != nil { @@ -650,7 +640,7 @@ func (t *Terminal) Loop() { } else if me.Double { // Double-click if my >= 2 { - if t.vset(my-2) && t.listIndex(t.cy) < t.merger.Length() { + if t.vset(my-2) && t.cy < t.merger.Length() { req(reqClose) } } |