mirror of
https://github.com/junegunn/fzf.git
synced 2025-08-03 21:52:00 -07:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9cb0cdb4ac | ||
|
448132c46c | ||
|
1476fc7f3b | ||
|
71a7b3a26f | ||
|
a47c06cb61 | ||
|
48e16edb47 | ||
|
c35d98dc42 | ||
|
8bead4ae34 | ||
|
1b6cb3532d | ||
|
0a0955755a |
2
install
2
install
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
version=0.9.1
|
version=0.9.2
|
||||||
|
|
||||||
cd $(dirname $BASH_SOURCE)
|
cd $(dirname $BASH_SOURCE)
|
||||||
fzf_base=$(pwd)
|
fzf_base=$(pwd)
|
||||||
|
@@ -19,6 +19,9 @@ git pull
|
|||||||
./install
|
./install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Otherwise, follow [the instruction][install] as before. You can also install
|
||||||
|
fzf using Homebrew if you prefer that way.
|
||||||
|
|
||||||
Motivations
|
Motivations
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
@@ -110,6 +113,7 @@ License
|
|||||||
|
|
||||||
[MIT](LICENSE)
|
[MIT](LICENSE)
|
||||||
|
|
||||||
|
[install]: https://github.com/junegunn/fzf#installation
|
||||||
[go]: https://golang.org/
|
[go]: https://golang.org/
|
||||||
[gil]: http://en.wikipedia.org/wiki/Global_Interpreter_Lock
|
[gil]: http://en.wikipedia.org/wiki/Global_Interpreter_Lock
|
||||||
[ncurses]: https://www.gnu.org/software/ncurses/
|
[ncurses]: https://www.gnu.org/software/ncurses/
|
||||||
|
@@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Current version
|
// Current version
|
||||||
const Version = "0.9.1"
|
const Version = "0.9.2"
|
||||||
|
|
||||||
// fzf events
|
// fzf events
|
||||||
const (
|
const (
|
||||||
|
@@ -421,6 +421,10 @@ func Clear() {
|
|||||||
C.clear()
|
C.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Endwin() {
|
||||||
|
C.endwin()
|
||||||
|
}
|
||||||
|
|
||||||
func Refresh() {
|
func Refresh() {
|
||||||
C.refresh()
|
C.refresh()
|
||||||
}
|
}
|
||||||
|
@@ -266,6 +266,17 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're not using extended search mode, --nth option becomes irrelevant
|
||||||
|
// if it contains the whole range
|
||||||
|
if opts.Mode == ModeFuzzy || len(opts.Nth) == 1 {
|
||||||
|
for _, r := range opts.Nth {
|
||||||
|
if r.begin == rangeEllipsis && r.end == rangeEllipsis {
|
||||||
|
opts.Nth = make([]Range, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseOptions parses command-line options
|
// ParseOptions parses command-line options
|
||||||
|
@@ -21,17 +21,47 @@ func TestSplitNth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ranges := splitNth("..3,1..,2..3,4..-1,-3..-2,..,2,-2")
|
ranges := splitNth("..3,1..,2..3,4..-1,-3..-2,..,2,-2,2..-2,1..-1")
|
||||||
if len(ranges) != 8 ||
|
if len(ranges) != 10 ||
|
||||||
ranges[0].begin != rangeEllipsis || ranges[0].end != 3 ||
|
ranges[0].begin != rangeEllipsis || ranges[0].end != 3 ||
|
||||||
ranges[1].begin != 1 || ranges[1].end != rangeEllipsis ||
|
ranges[1].begin != rangeEllipsis || ranges[1].end != rangeEllipsis ||
|
||||||
ranges[2].begin != 2 || ranges[2].end != 3 ||
|
ranges[2].begin != 2 || ranges[2].end != 3 ||
|
||||||
ranges[3].begin != 4 || ranges[3].end != -1 ||
|
ranges[3].begin != 4 || ranges[3].end != rangeEllipsis ||
|
||||||
ranges[4].begin != -3 || ranges[4].end != -2 ||
|
ranges[4].begin != -3 || ranges[4].end != -2 ||
|
||||||
ranges[5].begin != rangeEllipsis || ranges[5].end != rangeEllipsis ||
|
ranges[5].begin != rangeEllipsis || ranges[5].end != rangeEllipsis ||
|
||||||
ranges[6].begin != 2 || ranges[6].end != 2 ||
|
ranges[6].begin != 2 || ranges[6].end != 2 ||
|
||||||
ranges[7].begin != -2 || ranges[7].end != -2 {
|
ranges[7].begin != -2 || ranges[7].end != -2 ||
|
||||||
|
ranges[8].begin != 2 || ranges[8].end != -2 ||
|
||||||
|
ranges[9].begin != rangeEllipsis || ranges[9].end != rangeEllipsis {
|
||||||
t.Errorf("%s", ranges)
|
t.Errorf("%s", ranges)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIrrelevantNth(t *testing.T) {
|
||||||
|
{
|
||||||
|
opts := defaultOptions()
|
||||||
|
words := []string{"--nth", "..", "-x"}
|
||||||
|
parseOptions(opts, words)
|
||||||
|
if len(opts.Nth) != 0 {
|
||||||
|
t.Errorf("nth should be empty: %s", opts.Nth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, words := range [][]string{[]string{"--nth", "..,3"}, []string{"--nth", "3,1.."}, []string{"--nth", "..-1,1"}} {
|
||||||
|
{
|
||||||
|
opts := defaultOptions()
|
||||||
|
parseOptions(opts, words)
|
||||||
|
if len(opts.Nth) != 0 {
|
||||||
|
t.Errorf("nth should be empty: %s", opts.Nth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
opts := defaultOptions()
|
||||||
|
words = append(words, "-x")
|
||||||
|
parseOptions(opts, words)
|
||||||
|
if len(opts.Nth) != 2 {
|
||||||
|
t.Errorf("nth should not be empty: %s", opts.Nth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,11 +1,15 @@
|
|||||||
package fzf
|
package fzf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
C "github.com/junegunn/fzf/src/curses"
|
C "github.com/junegunn/fzf/src/curses"
|
||||||
@@ -58,6 +62,7 @@ func (a ByTimeOrder) Less(i, j int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var _spinner = []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
|
var _spinner = []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
|
||||||
|
var _runeWidths = make(map[rune]int)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
reqPrompt util.EventType = iota
|
reqPrompt util.EventType = iota
|
||||||
@@ -81,7 +86,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
prompt: opts.Prompt,
|
prompt: opts.Prompt,
|
||||||
tac: opts.Sort == 0,
|
tac: opts.Sort == 0,
|
||||||
reverse: opts.Reverse,
|
reverse: opts.Reverse,
|
||||||
cx: displayWidth(input),
|
cx: len(input),
|
||||||
cy: 0,
|
cy: 0,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
yanked: []rune{},
|
yanked: []rune{},
|
||||||
@@ -169,10 +174,22 @@ func (t *Terminal) output() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runeWidth(r rune, prefixWidth int) int {
|
||||||
|
if r == '\t' {
|
||||||
|
return 8 - prefixWidth%8
|
||||||
|
} else if w, found := _runeWidths[r]; found {
|
||||||
|
return w
|
||||||
|
} else {
|
||||||
|
w := runewidth.RuneWidth(r)
|
||||||
|
_runeWidths[r] = w
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func displayWidth(runes []rune) int {
|
func displayWidth(runes []rune) int {
|
||||||
l := 0
|
l := 0
|
||||||
for _, r := range runes {
|
for _, r := range runes {
|
||||||
l += runewidth.RuneWidth(r)
|
l += runeWidth(r, l)
|
||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
@@ -254,16 +271,27 @@ func (t *Terminal) printItem(item *Item, current bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func trimRight(runes []rune, width int) ([]rune, int) {
|
func trimRight(runes []rune, width int) ([]rune, int) {
|
||||||
currentWidth := displayWidth(runes)
|
// We start from the beginning to handle tab characters
|
||||||
trimmed := 0
|
l := 0
|
||||||
|
for idx, r := range runes {
|
||||||
for currentWidth > width && len(runes) > 0 {
|
l += runeWidth(r, l)
|
||||||
sz := len(runes)
|
if idx > 0 && l > width {
|
||||||
currentWidth -= runewidth.RuneWidth(runes[sz-1])
|
return runes[:idx], len(runes) - idx
|
||||||
runes = runes[:sz-1]
|
|
||||||
trimmed++
|
|
||||||
}
|
}
|
||||||
return runes, trimmed
|
}
|
||||||
|
return runes, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func displayWidthWithLimit(runes []rune, prefixWidth int, limit int) int {
|
||||||
|
l := 0
|
||||||
|
for _, r := range runes {
|
||||||
|
l += runeWidth(r, l+prefixWidth)
|
||||||
|
if l > limit {
|
||||||
|
// Early exit
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func trimLeft(runes []rune, width int) ([]rune, int32) {
|
func trimLeft(runes []rune, width int) ([]rune, int32) {
|
||||||
@@ -271,9 +299,9 @@ func trimLeft(runes []rune, width int) ([]rune, int32) {
|
|||||||
var trimmed int32
|
var trimmed int32
|
||||||
|
|
||||||
for currentWidth > width && len(runes) > 0 {
|
for currentWidth > width && len(runes) > 0 {
|
||||||
currentWidth -= runewidth.RuneWidth(runes[0])
|
|
||||||
runes = runes[1:]
|
runes = runes[1:]
|
||||||
trimmed++
|
trimmed++
|
||||||
|
currentWidth = displayWidthWithLimit(runes, 2, width)
|
||||||
}
|
}
|
||||||
return runes, trimmed
|
return runes, trimmed
|
||||||
}
|
}
|
||||||
@@ -323,18 +351,41 @@ func (*Terminal) printHighlighted(item *Item, bold bool, col1 int, col2 int) {
|
|||||||
|
|
||||||
sort.Sort(ByOrder(offsets))
|
sort.Sort(ByOrder(offsets))
|
||||||
var index int32
|
var index int32
|
||||||
|
var substr string
|
||||||
|
var prefixWidth int
|
||||||
for _, offset := range offsets {
|
for _, offset := range offsets {
|
||||||
b := util.Max32(index, offset[0])
|
b := util.Max32(index, offset[0])
|
||||||
e := util.Max32(index, offset[1])
|
e := util.Max32(index, offset[1])
|
||||||
C.CPrint(col1, bold, string(text[index:b]))
|
|
||||||
C.CPrint(col2, bold, string(text[b:e]))
|
substr, prefixWidth = processTabs(text[index:b], prefixWidth)
|
||||||
|
C.CPrint(col1, bold, substr)
|
||||||
|
|
||||||
|
substr, prefixWidth = processTabs(text[b:e], prefixWidth)
|
||||||
|
C.CPrint(col2, bold, substr)
|
||||||
|
|
||||||
index = e
|
index = e
|
||||||
}
|
}
|
||||||
if index < int32(len(text)) {
|
if index < int32(len(text)) {
|
||||||
C.CPrint(col1, bold, string(text[index:]))
|
substr, _ = processTabs(text[index:], prefixWidth)
|
||||||
|
C.CPrint(col1, bold, substr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func processTabs(runes []rune, prefixWidth int) (string, int) {
|
||||||
|
var strbuf bytes.Buffer
|
||||||
|
l := prefixWidth
|
||||||
|
for _, r := range runes {
|
||||||
|
w := runeWidth(r, l)
|
||||||
|
l += w
|
||||||
|
if r == '\t' {
|
||||||
|
strbuf.WriteString(strings.Repeat(" ", w))
|
||||||
|
} else {
|
||||||
|
strbuf.WriteRune(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strbuf.String(), l
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Terminal) printAll() {
|
func (t *Terminal) printAll() {
|
||||||
t.printList()
|
t.printList()
|
||||||
t.printInfo()
|
t.printInfo()
|
||||||
@@ -408,6 +459,15 @@ func (t *Terminal) Loop() {
|
|||||||
<-timer.C
|
<-timer.C
|
||||||
t.reqBox.Set(reqRefresh, nil)
|
t.reqBox.Set(reqRefresh, nil)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
resizeChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(resizeChan, syscall.SIGWINCH)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
<-resizeChan
|
||||||
|
t.reqBox.Set(reqRedraw, nil)
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -427,6 +487,8 @@ func (t *Terminal) Loop() {
|
|||||||
t.suppress = false
|
t.suppress = false
|
||||||
case reqRedraw:
|
case reqRedraw:
|
||||||
C.Clear()
|
C.Clear()
|
||||||
|
C.Endwin()
|
||||||
|
C.Refresh()
|
||||||
t.printAll()
|
t.printAll()
|
||||||
case reqClose:
|
case reqClose:
|
||||||
C.Close()
|
C.Close()
|
||||||
|
@@ -28,22 +28,32 @@ type Token struct {
|
|||||||
prefixLength int
|
prefixLength int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newRange(begin int, end int) Range {
|
||||||
|
if begin == 1 {
|
||||||
|
begin = rangeEllipsis
|
||||||
|
}
|
||||||
|
if end == -1 {
|
||||||
|
end = rangeEllipsis
|
||||||
|
}
|
||||||
|
return Range{begin, end}
|
||||||
|
}
|
||||||
|
|
||||||
// ParseRange parses nth-expression and returns the corresponding Range object
|
// ParseRange parses nth-expression and returns the corresponding Range object
|
||||||
func ParseRange(str *string) (Range, bool) {
|
func ParseRange(str *string) (Range, bool) {
|
||||||
if (*str) == ".." {
|
if (*str) == ".." {
|
||||||
return Range{rangeEllipsis, rangeEllipsis}, true
|
return newRange(rangeEllipsis, rangeEllipsis), true
|
||||||
} else if strings.HasPrefix(*str, "..") {
|
} else if strings.HasPrefix(*str, "..") {
|
||||||
end, err := strconv.Atoi((*str)[2:])
|
end, err := strconv.Atoi((*str)[2:])
|
||||||
if err != nil || end == 0 {
|
if err != nil || end == 0 {
|
||||||
return Range{}, false
|
return Range{}, false
|
||||||
}
|
}
|
||||||
return Range{rangeEllipsis, end}, true
|
return newRange(rangeEllipsis, end), true
|
||||||
} else if strings.HasSuffix(*str, "..") {
|
} else if strings.HasSuffix(*str, "..") {
|
||||||
begin, err := strconv.Atoi((*str)[:len(*str)-2])
|
begin, err := strconv.Atoi((*str)[:len(*str)-2])
|
||||||
if err != nil || begin == 0 {
|
if err != nil || begin == 0 {
|
||||||
return Range{}, false
|
return Range{}, false
|
||||||
}
|
}
|
||||||
return Range{begin, rangeEllipsis}, true
|
return newRange(begin, rangeEllipsis), true
|
||||||
} else if strings.Contains(*str, "..") {
|
} else if strings.Contains(*str, "..") {
|
||||||
ns := strings.Split(*str, "..")
|
ns := strings.Split(*str, "..")
|
||||||
if len(ns) != 2 {
|
if len(ns) != 2 {
|
||||||
@@ -51,17 +61,17 @@ func ParseRange(str *string) (Range, bool) {
|
|||||||
}
|
}
|
||||||
begin, err1 := strconv.Atoi(ns[0])
|
begin, err1 := strconv.Atoi(ns[0])
|
||||||
end, err2 := strconv.Atoi(ns[1])
|
end, err2 := strconv.Atoi(ns[1])
|
||||||
if err1 != nil || err2 != nil {
|
if err1 != nil || err2 != nil || begin == 0 || end == 0 {
|
||||||
return Range{}, false
|
return Range{}, false
|
||||||
}
|
}
|
||||||
return Range{begin, end}, true
|
return newRange(begin, end), true
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := strconv.Atoi(*str)
|
n, err := strconv.Atoi(*str)
|
||||||
if err != nil || n == 0 {
|
if err != nil || n == 0 {
|
||||||
return Range{}, false
|
return Range{}, false
|
||||||
}
|
}
|
||||||
return Range{n, n}, true
|
return newRange(n, n), true
|
||||||
}
|
}
|
||||||
|
|
||||||
func withPrefixLengths(tokens []string, begin int) []Token {
|
func withPrefixLengths(tokens []string, begin int) []Token {
|
||||||
|
@@ -5,6 +5,7 @@ require 'rest_client'
|
|||||||
|
|
||||||
if ARGV.length < 3
|
if ARGV.length < 3
|
||||||
puts "usage: #$0 <token> <version> <files...>"
|
puts "usage: #$0 <token> <version> <files...>"
|
||||||
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
token, version, *files = ARGV
|
token, version, *files = ARGV
|
||||||
|
111
test/test_go.rb
111
test/test_go.rb
@@ -9,13 +9,36 @@ class NilClass
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module Temp
|
||||||
|
def readonce
|
||||||
|
name = self.class::TEMPNAME
|
||||||
|
waited = 0
|
||||||
|
while waited < 5
|
||||||
|
begin
|
||||||
|
data = File.read(name)
|
||||||
|
return data unless data.empty?
|
||||||
|
rescue
|
||||||
|
sleep 0.1
|
||||||
|
waited += 0.1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
raise "failed to read tempfile"
|
||||||
|
ensure
|
||||||
|
while File.exists? name
|
||||||
|
File.unlink name rescue nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class Tmux
|
class Tmux
|
||||||
|
include Temp
|
||||||
|
|
||||||
TEMPNAME = '/tmp/fzf-test.txt'
|
TEMPNAME = '/tmp/fzf-test.txt'
|
||||||
|
|
||||||
attr_reader :win
|
attr_reader :win
|
||||||
|
|
||||||
def initialize shell = 'bash'
|
def initialize shell = 'bash'
|
||||||
@win = go("new-window -d -P -F '#I' 'PS1= bash --rcfile ~/.fzf.#{shell}'").first
|
@win = go("new-window -d -P -F '#I' 'PS1=FIN PROMPT_COMMAND= bash --rcfile ~/.fzf.#{shell}'").first
|
||||||
@lines = `tput lines`.chomp.to_i
|
@lines = `tput lines`.chomp.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -40,7 +63,7 @@ class Tmux
|
|||||||
def capture
|
def capture
|
||||||
go("capture-pane -t #{win} \\; save-buffer #{TEMPNAME}")
|
go("capture-pane -t #{win} \\; save-buffer #{TEMPNAME}")
|
||||||
raise "Window not found" if $?.exitstatus != 0
|
raise "Window not found" if $?.exitstatus != 0
|
||||||
File.read(TEMPNAME).split($/)[0, @lines].reverse.drop_while(&:empty?).reverse
|
readonce.split($/)[0, @lines].reverse.drop_while(&:empty?).reverse
|
||||||
end
|
end
|
||||||
|
|
||||||
def until timeout = 1
|
def until timeout = 1
|
||||||
@@ -71,36 +94,16 @@ private
|
|||||||
end
|
end
|
||||||
|
|
||||||
class TestGoFZF < MiniTest::Unit::TestCase
|
class TestGoFZF < MiniTest::Unit::TestCase
|
||||||
|
include Temp
|
||||||
|
|
||||||
|
TEMPNAME = '/tmp/output'
|
||||||
|
|
||||||
attr_reader :tmux
|
attr_reader :tmux
|
||||||
|
|
||||||
def tempname
|
|
||||||
'/tmp/output'
|
|
||||||
end
|
|
||||||
|
|
||||||
def rmtemp
|
|
||||||
while File.exists? tempname
|
|
||||||
File.unlink tempname rescue nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def readtemp
|
|
||||||
waited = 0
|
|
||||||
while waited < 5
|
|
||||||
begin
|
|
||||||
return File.read(tempname)
|
|
||||||
rescue
|
|
||||||
sleep 0.1
|
|
||||||
waited += 0.1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
raise "failed to read tempfile"
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
ENV.delete 'FZF_DEFAULT_OPTS'
|
ENV.delete 'FZF_DEFAULT_OPTS'
|
||||||
ENV.delete 'FZF_DEFAULT_COMMAND'
|
ENV.delete 'FZF_DEFAULT_COMMAND'
|
||||||
@tmux = Tmux.new
|
@tmux = Tmux.new
|
||||||
rmtemp
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def teardown
|
def teardown
|
||||||
@@ -108,7 +111,7 @@ class TestGoFZF < MiniTest::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_vanilla
|
def test_vanilla
|
||||||
tmux.send_keys "seq 1 100000 | fzf > #{tempname}", :Enter
|
tmux.send_keys "seq 1 100000 | fzf > #{TEMPNAME}", :Enter
|
||||||
tmux.until(10) { |lines| lines.last =~ /^>/ && lines[-2] =~ /^ 100000/ }
|
tmux.until(10) { |lines| lines.last =~ /^>/ && lines[-2] =~ /^ 100000/ }
|
||||||
lines = tmux.capture
|
lines = tmux.capture
|
||||||
assert_equal ' 2', lines[-4]
|
assert_equal ' 2', lines[-4]
|
||||||
@@ -127,16 +130,16 @@ class TestGoFZF < MiniTest::Unit::TestCase
|
|||||||
|
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.close
|
tmux.close
|
||||||
assert_equal '1391', readtemp.chomp
|
assert_equal '1391', readonce.chomp
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_fzf_default_command
|
def test_fzf_default_command
|
||||||
tmux.send_keys "FZF_DEFAULT_COMMAND='echo hello' fzf > #{tempname}", :Enter
|
tmux.send_keys "FZF_DEFAULT_COMMAND='echo hello' fzf > #{TEMPNAME}", :Enter
|
||||||
tmux.until { |lines| lines.last =~ /^>/ }
|
tmux.until { |lines| lines.last =~ /^>/ }
|
||||||
|
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.close
|
tmux.close
|
||||||
assert_equal 'hello', readtemp.chomp
|
assert_equal 'hello', readonce.chomp
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_key_bindings
|
def test_key_bindings
|
||||||
@@ -206,7 +209,7 @@ class TestGoFZF < MiniTest::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_multi_order
|
def test_multi_order
|
||||||
tmux.send_keys "seq 1 10 | fzf --multi > #{tempname} && echo -n done", :Enter
|
tmux.send_keys "seq 1 10 | fzf --multi > #{TEMPNAME}", :Enter
|
||||||
tmux.until { |lines| lines.last =~ /^>/ }
|
tmux.until { |lines| lines.last =~ /^>/ }
|
||||||
|
|
||||||
tmux.send_keys :Tab, :Up, :Up, :Tab, :Tab, :Tab, # 3, 2
|
tmux.send_keys :Tab, :Up, :Up, :Tab, :Tab, :Tab, # 3, 2
|
||||||
@@ -214,18 +217,16 @@ class TestGoFZF < MiniTest::Unit::TestCase
|
|||||||
:PgUp, 'C-J', :Down, :Tab, :Tab # 8, 7
|
:PgUp, 'C-J', :Down, :Tab, :Tab # 8, 7
|
||||||
tmux.until { |lines| lines[-2].include? '(6)' }
|
tmux.until { |lines| lines[-2].include? '(6)' }
|
||||||
tmux.send_keys "C-M"
|
tmux.send_keys "C-M"
|
||||||
tmux.until { |lines| lines[-1].include?('done') }
|
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||||
assert_equal %w[3 2 5 6 8 7], readtemp.split($/)
|
assert_equal %w[3 2 5 6 8 7], readonce.split($/)
|
||||||
tmux.close
|
tmux.close
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_with_nth
|
def test_with_nth
|
||||||
[true, false].each do |multi|
|
[true, false].each do |multi|
|
||||||
rmtemp
|
|
||||||
|
|
||||||
tmux.send_keys "(echo ' 1st 2nd 3rd/';
|
tmux.send_keys "(echo ' 1st 2nd 3rd/';
|
||||||
echo ' first second third/') |
|
echo ' first second third/') |
|
||||||
fzf #{"--multi" if multi} -x --nth 2 --with-nth 2,-1,1 > #{tempname} && echo -n done",
|
fzf #{"--multi" if multi} -x --nth 2 --with-nth 2,-1,1 > #{TEMPNAME}",
|
||||||
:Enter
|
:Enter
|
||||||
tmux.until { |lines| lines[-2].include?('2/2') }
|
tmux.until { |lines| lines[-2].include?('2/2') }
|
||||||
|
|
||||||
@@ -237,42 +238,48 @@ class TestGoFZF < MiniTest::Unit::TestCase
|
|||||||
# However, the output must not be transformed
|
# However, the output must not be transformed
|
||||||
if multi
|
if multi
|
||||||
tmux.send_keys :BTab, :BTab, :Enter
|
tmux.send_keys :BTab, :BTab, :Enter
|
||||||
tmux.until { |lines| lines[-1].include?('done') }
|
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||||
assert_equal [' 1st 2nd 3rd/', ' first second third/'], readtemp.split($/)
|
assert_equal [' 1st 2nd 3rd/', ' first second third/'], readonce.split($/)
|
||||||
else
|
else
|
||||||
tmux.send_keys '^', '3'
|
tmux.send_keys '^', '3'
|
||||||
tmux.until { |lines| lines[-2].include?('1/2') }
|
tmux.until { |lines| lines[-2].include?('1/2') }
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.until { |lines| lines[-1].include?('done') }
|
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||||
assert_equal [' 1st 2nd 3rd/'], readtemp.split($/)
|
assert_equal [' 1st 2nd 3rd/'], readonce.split($/)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_scroll
|
def test_scroll
|
||||||
[true, false].each do |rev|
|
[true, false].each do |rev|
|
||||||
rmtemp
|
tmux.send_keys "seq 1 100 | fzf #{'--reverse' if rev} > #{TEMPNAME}", :Enter
|
||||||
|
tmux.until { |lines| lines.include? ' 100/100' }
|
||||||
tmux.send_keys "seq 1 100 | fzf #{'--reverse' if rev} > #{tempname} && echo -n done", :Enter
|
|
||||||
tmux.until { |lines| rev ? lines.first == '>' : lines.last == '>' }
|
|
||||||
tmux.send_keys *110.times.map { rev ? :Down : :Up }
|
tmux.send_keys *110.times.map { rev ? :Down : :Up }
|
||||||
tmux.until { |lines| lines.include? '> 100' }
|
tmux.until { |lines| lines.include? '> 100' }
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.until { |lines| lines[-1].include?('done') }
|
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||||
assert_equal '100', readtemp.chomp
|
assert_equal '100', readonce.chomp
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_select_1
|
def test_select_1
|
||||||
tmux.send_keys "seq 1 100 | fzf --with-nth ..,.. --print-query -q 5555 -1 > #{tempname} && echo -n done", :Enter
|
tmux.send_keys "seq 1 100 | fzf --with-nth ..,.. --print-query -q 5555 -1 > #{TEMPNAME}", :Enter
|
||||||
tmux.until { |lines| lines[-1].include?('done') }
|
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||||
assert_equal ['5555', '55'], readtemp.split($/)
|
assert_equal ['5555', '55'], readonce.split($/)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_exit_0
|
def test_exit_0
|
||||||
tmux.send_keys "seq 1 100 | fzf --with-nth ..,.. --print-query -q 555555 -0 > #{tempname} && echo -n done", :Enter
|
tmux.send_keys "seq 1 100 | fzf --with-nth ..,.. --print-query -q 555555 -0 > #{TEMPNAME}", :Enter
|
||||||
tmux.until { |lines| lines[-1].include?('done') }
|
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||||
assert_equal ['555555'], readtemp.split($/)
|
assert_equal ['555555'], readonce.split($/)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_query_unicode
|
||||||
|
tmux.send_keys "(echo abc; echo 가나다) | fzf --query 가다 > #{TEMPNAME}", :Enter
|
||||||
|
tmux.until { |lines| lines.last.start_with? '>' }
|
||||||
|
tmux.send_keys :Enter
|
||||||
|
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||||
|
assert_equal ['가나다'], readonce.split($/)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user