mirror of
https://github.com/junegunn/fzf.git
synced 2025-08-01 12:42:01 -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
|
||||
|
||||
version=0.9.1
|
||||
version=0.9.2
|
||||
|
||||
cd $(dirname $BASH_SOURCE)
|
||||
fzf_base=$(pwd)
|
||||
|
@@ -19,6 +19,9 @@ git pull
|
||||
./install
|
||||
```
|
||||
|
||||
Otherwise, follow [the instruction][install] as before. You can also install
|
||||
fzf using Homebrew if you prefer that way.
|
||||
|
||||
Motivations
|
||||
-----------
|
||||
|
||||
@@ -110,6 +113,7 @@ License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[install]: https://github.com/junegunn/fzf#installation
|
||||
[go]: https://golang.org/
|
||||
[gil]: http://en.wikipedia.org/wiki/Global_Interpreter_Lock
|
||||
[ncurses]: https://www.gnu.org/software/ncurses/
|
||||
|
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
// Current version
|
||||
const Version = "0.9.1"
|
||||
const Version = "0.9.2"
|
||||
|
||||
// fzf events
|
||||
const (
|
||||
|
@@ -421,6 +421,10 @@ func Clear() {
|
||||
C.clear()
|
||||
}
|
||||
|
||||
func Endwin() {
|
||||
C.endwin()
|
||||
}
|
||||
|
||||
func 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
|
||||
|
@@ -21,17 +21,47 @@ func TestSplitNth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
{
|
||||
ranges := splitNth("..3,1..,2..3,4..-1,-3..-2,..,2,-2")
|
||||
if len(ranges) != 8 ||
|
||||
ranges := splitNth("..3,1..,2..3,4..-1,-3..-2,..,2,-2,2..-2,1..-1")
|
||||
if len(ranges) != 10 ||
|
||||
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[3].begin != 4 || ranges[3].end != -1 ||
|
||||
ranges[3].begin != 4 || ranges[3].end != rangeEllipsis ||
|
||||
ranges[4].begin != -3 || ranges[4].end != -2 ||
|
||||
ranges[5].begin != rangeEllipsis || ranges[5].end != rangeEllipsis ||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
C "github.com/junegunn/fzf/src/curses"
|
||||
@@ -58,6 +62,7 @@ func (a ByTimeOrder) Less(i, j int) bool {
|
||||
}
|
||||
|
||||
var _spinner = []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
|
||||
var _runeWidths = make(map[rune]int)
|
||||
|
||||
const (
|
||||
reqPrompt util.EventType = iota
|
||||
@@ -81,7 +86,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
prompt: opts.Prompt,
|
||||
tac: opts.Sort == 0,
|
||||
reverse: opts.Reverse,
|
||||
cx: displayWidth(input),
|
||||
cx: len(input),
|
||||
cy: 0,
|
||||
offset: 0,
|
||||
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 {
|
||||
l := 0
|
||||
for _, r := range runes {
|
||||
l += runewidth.RuneWidth(r)
|
||||
l += runeWidth(r, l)
|
||||
}
|
||||
return l
|
||||
}
|
||||
@@ -254,16 +271,27 @@ func (t *Terminal) printItem(item *Item, current bool) {
|
||||
}
|
||||
|
||||
func trimRight(runes []rune, width int) ([]rune, int) {
|
||||
currentWidth := displayWidth(runes)
|
||||
trimmed := 0
|
||||
|
||||
for currentWidth > width && len(runes) > 0 {
|
||||
sz := len(runes)
|
||||
currentWidth -= runewidth.RuneWidth(runes[sz-1])
|
||||
runes = runes[:sz-1]
|
||||
trimmed++
|
||||
// We start from the beginning to handle tab characters
|
||||
l := 0
|
||||
for idx, r := range runes {
|
||||
l += runeWidth(r, l)
|
||||
if idx > 0 && l > width {
|
||||
return runes[:idx], len(runes) - idx
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@@ -271,9 +299,9 @@ func trimLeft(runes []rune, width int) ([]rune, int32) {
|
||||
var trimmed int32
|
||||
|
||||
for currentWidth > width && len(runes) > 0 {
|
||||
currentWidth -= runewidth.RuneWidth(runes[0])
|
||||
runes = runes[1:]
|
||||
trimmed++
|
||||
currentWidth = displayWidthWithLimit(runes, 2, width)
|
||||
}
|
||||
return runes, trimmed
|
||||
}
|
||||
@@ -323,18 +351,41 @@ func (*Terminal) printHighlighted(item *Item, bold bool, col1 int, col2 int) {
|
||||
|
||||
sort.Sort(ByOrder(offsets))
|
||||
var index int32
|
||||
var substr string
|
||||
var prefixWidth int
|
||||
for _, offset := range offsets {
|
||||
b := util.Max32(index, offset[0])
|
||||
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
|
||||
}
|
||||
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() {
|
||||
t.printList()
|
||||
t.printInfo()
|
||||
@@ -408,6 +459,15 @@ func (t *Terminal) Loop() {
|
||||
<-timer.C
|
||||
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() {
|
||||
@@ -427,6 +487,8 @@ func (t *Terminal) Loop() {
|
||||
t.suppress = false
|
||||
case reqRedraw:
|
||||
C.Clear()
|
||||
C.Endwin()
|
||||
C.Refresh()
|
||||
t.printAll()
|
||||
case reqClose:
|
||||
C.Close()
|
||||
|
@@ -28,22 +28,32 @@ type Token struct {
|
||||
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
|
||||
func ParseRange(str *string) (Range, bool) {
|
||||
if (*str) == ".." {
|
||||
return Range{rangeEllipsis, rangeEllipsis}, true
|
||||
return newRange(rangeEllipsis, rangeEllipsis), true
|
||||
} else if strings.HasPrefix(*str, "..") {
|
||||
end, err := strconv.Atoi((*str)[2:])
|
||||
if err != nil || end == 0 {
|
||||
return Range{}, false
|
||||
}
|
||||
return Range{rangeEllipsis, end}, true
|
||||
return newRange(rangeEllipsis, end), true
|
||||
} else if strings.HasSuffix(*str, "..") {
|
||||
begin, err := strconv.Atoi((*str)[:len(*str)-2])
|
||||
if err != nil || begin == 0 {
|
||||
return Range{}, false
|
||||
}
|
||||
return Range{begin, rangeEllipsis}, true
|
||||
return newRange(begin, rangeEllipsis), true
|
||||
} else if strings.Contains(*str, "..") {
|
||||
ns := strings.Split(*str, "..")
|
||||
if len(ns) != 2 {
|
||||
@@ -51,17 +61,17 @@ func ParseRange(str *string) (Range, bool) {
|
||||
}
|
||||
begin, err1 := strconv.Atoi(ns[0])
|
||||
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{begin, end}, true
|
||||
return newRange(begin, end), true
|
||||
}
|
||||
|
||||
n, err := strconv.Atoi(*str)
|
||||
if err != nil || n == 0 {
|
||||
return Range{}, false
|
||||
}
|
||||
return Range{n, n}, true
|
||||
return newRange(n, n), true
|
||||
}
|
||||
|
||||
func withPrefixLengths(tokens []string, begin int) []Token {
|
||||
|
@@ -5,6 +5,7 @@ require 'rest_client'
|
||||
|
||||
if ARGV.length < 3
|
||||
puts "usage: #$0 <token> <version> <files...>"
|
||||
exit 1
|
||||
end
|
||||
|
||||
token, version, *files = ARGV
|
||||
|
111
test/test_go.rb
111
test/test_go.rb
@@ -9,13 +9,36 @@ class NilClass
|
||||
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
|
||||
include Temp
|
||||
|
||||
TEMPNAME = '/tmp/fzf-test.txt'
|
||||
|
||||
attr_reader :win
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
@@ -40,7 +63,7 @@ class Tmux
|
||||
def capture
|
||||
go("capture-pane -t #{win} \\; save-buffer #{TEMPNAME}")
|
||||
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
|
||||
|
||||
def until timeout = 1
|
||||
@@ -71,36 +94,16 @@ private
|
||||
end
|
||||
|
||||
class TestGoFZF < MiniTest::Unit::TestCase
|
||||
include Temp
|
||||
|
||||
TEMPNAME = '/tmp/output'
|
||||
|
||||
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
|
||||
ENV.delete 'FZF_DEFAULT_OPTS'
|
||||
ENV.delete 'FZF_DEFAULT_COMMAND'
|
||||
@tmux = Tmux.new
|
||||
rmtemp
|
||||
end
|
||||
|
||||
def teardown
|
||||
@@ -108,7 +111,7 @@ class TestGoFZF < MiniTest::Unit::TestCase
|
||||
end
|
||||
|
||||
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/ }
|
||||
lines = tmux.capture
|
||||
assert_equal ' 2', lines[-4]
|
||||
@@ -127,16 +130,16 @@ class TestGoFZF < MiniTest::Unit::TestCase
|
||||
|
||||
tmux.send_keys :Enter
|
||||
tmux.close
|
||||
assert_equal '1391', readtemp.chomp
|
||||
assert_equal '1391', readonce.chomp
|
||||
end
|
||||
|
||||
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.send_keys :Enter
|
||||
tmux.close
|
||||
assert_equal 'hello', readtemp.chomp
|
||||
assert_equal 'hello', readonce.chomp
|
||||
end
|
||||
|
||||
def test_key_bindings
|
||||
@@ -206,7 +209,7 @@ class TestGoFZF < MiniTest::Unit::TestCase
|
||||
end
|
||||
|
||||
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.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
|
||||
tmux.until { |lines| lines[-2].include? '(6)' }
|
||||
tmux.send_keys "C-M"
|
||||
tmux.until { |lines| lines[-1].include?('done') }
|
||||
assert_equal %w[3 2 5 6 8 7], readtemp.split($/)
|
||||
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||
assert_equal %w[3 2 5 6 8 7], readonce.split($/)
|
||||
tmux.close
|
||||
end
|
||||
|
||||
def test_with_nth
|
||||
[true, false].each do |multi|
|
||||
rmtemp
|
||||
|
||||
tmux.send_keys "(echo ' 1st 2nd 3rd/';
|
||||
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
|
||||
tmux.until { |lines| lines[-2].include?('2/2') }
|
||||
|
||||
@@ -237,42 +238,48 @@ class TestGoFZF < MiniTest::Unit::TestCase
|
||||
# However, the output must not be transformed
|
||||
if multi
|
||||
tmux.send_keys :BTab, :BTab, :Enter
|
||||
tmux.until { |lines| lines[-1].include?('done') }
|
||||
assert_equal [' 1st 2nd 3rd/', ' first second third/'], readtemp.split($/)
|
||||
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||
assert_equal [' 1st 2nd 3rd/', ' first second third/'], readonce.split($/)
|
||||
else
|
||||
tmux.send_keys '^', '3'
|
||||
tmux.until { |lines| lines[-2].include?('1/2') }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| lines[-1].include?('done') }
|
||||
assert_equal [' 1st 2nd 3rd/'], readtemp.split($/)
|
||||
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||
assert_equal [' 1st 2nd 3rd/'], readonce.split($/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_scroll
|
||||
[true, false].each do |rev|
|
||||
rmtemp
|
||||
|
||||
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 "seq 1 100 | fzf #{'--reverse' if rev} > #{TEMPNAME}", :Enter
|
||||
tmux.until { |lines| lines.include? ' 100/100' }
|
||||
tmux.send_keys *110.times.map { rev ? :Down : :Up }
|
||||
tmux.until { |lines| lines.include? '> 100' }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| lines[-1].include?('done') }
|
||||
assert_equal '100', readtemp.chomp
|
||||
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||
assert_equal '100', readonce.chomp
|
||||
end
|
||||
end
|
||||
|
||||
def test_select_1
|
||||
tmux.send_keys "seq 1 100 | fzf --with-nth ..,.. --print-query -q 5555 -1 > #{tempname} && echo -n done", :Enter
|
||||
tmux.until { |lines| lines[-1].include?('done') }
|
||||
assert_equal ['5555', '55'], readtemp.split($/)
|
||||
tmux.send_keys "seq 1 100 | fzf --with-nth ..,.. --print-query -q 5555 -1 > #{TEMPNAME}", :Enter
|
||||
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||
assert_equal ['5555', '55'], readonce.split($/)
|
||||
end
|
||||
|
||||
def test_exit_0
|
||||
tmux.send_keys "seq 1 100 | fzf --with-nth ..,.. --print-query -q 555555 -0 > #{tempname} && echo -n done", :Enter
|
||||
tmux.until { |lines| lines[-1].include?('done') }
|
||||
assert_equal ['555555'], readtemp.split($/)
|
||||
tmux.send_keys "seq 1 100 | fzf --with-nth ..,.. --print-query -q 555555 -0 > #{TEMPNAME}", :Enter
|
||||
tmux.until { |lines| lines[-1].include?('FIN') }
|
||||
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
|
||||
|
||||
|
Reference in New Issue
Block a user