Fix issues in tcell renderer and Windows build

- Fix display of CJK wide characters
- Fix horizontal offset of header lines
- Add support for keys with ALT modifier, shift-tab, page-up and down
- Fix util.ExecCommand to properly parse command-line arguments
- Fix redraw on resize
- Implement Pause/Resume for execute action
- Remove runtime check of GOOS
- Change exit status to 2 when tcell failed to start
- TBD: Travis CI build for tcell renderer
    - Pending. tcell cannot reliably ingest keys from tmux send-keys
pull/730/head
Junegunn Choi 8 years ago
parent 26895da969
commit 898d8d94c8
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627

@ -1,6 +1,10 @@
language: ruby
rvm:
- 2.2.0
matrix:
include:
- env: TAGS=
rvm: 2.2.0
# - env: TAGS=tcell
# rvm: 2.2.0
install:
- sudo apt-get update

@ -52,7 +52,7 @@ android-build: $(SRCDIR)
rm -f $(RELEASEARM7)
test: deps
SHELL=/bin/sh go test -v ./...
SHELL=/bin/sh go test -v -tags "$(TAGS)" ./...
install: $(BINDIR)/fzf

@ -4,7 +4,6 @@ import (
"bufio"
"io"
"os"
"runtime"
"github.com/junegunn/fzf/src/util"
)
@ -44,7 +43,7 @@ func (r *Reader) feed(src io.Reader) {
if len(bytea) > 0 {
if err == nil {
// get rid of carriage return if under Windows:
if runtime.GOOS == "windows" && byteaLen >= 2 && bytea[byteaLen-2] == byte('\r') {
if util.IsWindows() && byteaLen >= 2 && bytea[byteaLen-2] == byte('\r') {
bytea = bytea[:byteaLen-2]
} else {
bytea = bytea[:byteaLen-1]

@ -7,6 +7,7 @@ import (
"os/signal"
"regexp"
"sort"
"strconv"
"strings"
"sync"
"syscall"
@ -919,6 +920,9 @@ func keyMatch(key int, event tui.Event) bool {
}
func quoteEntry(entry string) string {
if util.IsWindows() {
return strconv.Quote(strings.Replace(entry, "\"", "\\\"", -1))
}
return "'" + strings.Replace(entry, "'", "'\\''", -1) + "'"
}
@ -982,6 +986,9 @@ func (t *Terminal) executeCommand(template string, items []*Item) {
cmd.Stderr = os.Stderr
tui.Pause()
cmd.Run()
if tui.Resume() {
t.printAll()
}
t.refresh()
}

@ -138,6 +138,10 @@ func Pause() {
C.endwin()
}
func Resume() bool {
return false
}
func Close() {
C.endwin()
C.delscreen(_screen)

@ -13,6 +13,8 @@ import (
"github.com/gdamore/tcell"
"github.com/gdamore/tcell/encoding"
"github.com/junegunn/go-runewidth"
)
type ColorPair [2]Color
@ -108,21 +110,32 @@ func (a Attr) Merge(b Attr) Attr {
var (
_screen tcell.Screen
_mouse bool
)
func Init(theme *ColorTheme, black bool, mouse bool) {
encoding.Register()
func initScreen() {
s, e := tcell.NewScreen()
if e != nil {
fmt.Fprintf(os.Stderr, "%v\n", e)
os.Exit(1)
os.Exit(2)
}
if e = s.Init(); e != nil {
fmt.Fprintf(os.Stderr, "%v\n", e)
os.Exit(1)
os.Exit(2)
}
if _mouse {
s.EnableMouse()
} else {
s.DisableMouse()
}
_screen = s
}
func Init(theme *ColorTheme, black bool, mouse bool) {
encoding.Register()
_mouse = mouse
initScreen()
_color = theme != nil
if _color {
@ -139,12 +152,6 @@ func Init(theme *ColorTheme, black bool, mouse bool) {
ColSelected = ColorPair{theme.Selected, theme.DarkBg}
ColHeader = ColorPair{theme.Header, theme.Bg}
ColBorder = ColorPair{theme.Border, theme.Bg}
if mouse {
_screen.EnableMouse()
} else {
_screen.DisableMouse()
}
}
func MaxX() int {
@ -162,6 +169,7 @@ func (w *Window) win() *WindowTcell {
}
func Clear() {
_screen.Sync()
_screen.Clear()
}
@ -211,6 +219,7 @@ func GetChar() Event {
// process keyboard:
case *tcell.EventKey:
alt := (ev.Modifiers() & tcell.ModAlt) > 0
switch ev.Key() {
case tcell.KeyCtrlA:
return Event{CtrlA, 0, nil}
@ -233,6 +242,9 @@ func GetChar() Event {
case tcell.KeyCtrlL:
return Event{CtrlL, 0, nil}
case tcell.KeyCtrlM:
if alt {
return Event{AltEnter, 0, nil}
}
return Event{CtrlM, 0, nil}
case tcell.KeyCtrlN:
return Event{CtrlN, 0, nil}
@ -261,6 +273,9 @@ func GetChar() Event {
case tcell.KeyCtrlZ:
return Event{CtrlZ, 0, nil}
case tcell.KeyBackspace, tcell.KeyBackspace2:
if alt {
return Event{AltBS, 0, nil}
}
return Event{BSpace, 0, nil}
case tcell.KeyUp:
@ -278,13 +293,15 @@ func GetChar() Event {
return Event{Del, 0, nil}
case tcell.KeyEnd:
return Event{End, 0, nil}
/*case tcell.KeyPgUp:
case tcell.KeyPgUp:
return Event{PgUp, 0, nil}
case tcell.KeyPgdn:
return Event{PgDn, 0, nil}*/
case tcell.KeyPgDn:
return Event{PgDn, 0, nil}
case tcell.KeyTab:
return Event{Tab, 0, nil}
case tcell.KeyBacktab:
return Event{BTab, 0, nil}
case tcell.KeyF1:
return Event{F1, 0, nil}
@ -313,7 +330,19 @@ func GetChar() Event {
// ev.Ch doesn't work for some reason for space:
case tcell.KeyRune:
return Event{Rune, ev.Rune(), nil}
r := ev.Rune()
if alt {
switch r {
case ' ':
return Event{AltSpace, 0, nil}
case '/':
return Event{AltSlash, 0, nil}
}
if r >= 'a' && r <= 'z' {
return Event{AltA + int(r) - 'a', 0, nil}
}
}
return Event{Rune, r, nil}
case tcell.KeyEsc:
return Event{ESC, 0, nil}
@ -325,7 +354,12 @@ func GetChar() Event {
}
func Pause() {
// TODO
_screen.Fini()
}
func Resume() bool {
initScreen()
return true
}
func Close() {
@ -391,11 +425,10 @@ func (w *Window) Move(y int, x int) {
func (w *Window) MoveAndClear(y int, x int) {
w.Move(y, x)
r, _ := utf8.DecodeRuneInString(" ")
for i := w.win().LastX; i < w.Width; i++ {
_screen.SetContent(i+w.Left, w.win().LastY+w.Top, r, nil, ColDefault.style())
_screen.SetContent(i+w.Left, w.win().LastY+w.Top, rune(' '), nil, ColDefault.style())
}
w.win().LastX = 0
w.win().LastX = x
}
func (w *Window) Print(text string) {
@ -439,7 +472,7 @@ func (w *Window) PrintString(text string, pair ColorPair, a Attr) {
if xPos < (w.Left+w.Width) && yPos < (w.Top+w.Height) {
_screen.SetContent(xPos, yPos, r, nil, style)
}
lx++
lx += runewidth.RuneWidth(r)
}
}
w.win().LastX += lx
@ -482,7 +515,7 @@ func (w *Window) FillString(text string, pair ColorPair, a Attr) bool {
}
_screen.SetContent(xPos, yPos, r, nil, style)
lx++
lx += runewidth.RuneWidth(r)
}
}
w.win().LastX += lx

@ -15,3 +15,8 @@ func ExecCommand(command string) *exec.Cmd {
}
return exec.Command(shell, "-c", command)
}
// IsWindows returns true on Windows
func IsWindows() bool {
return false
}

@ -5,6 +5,8 @@ package util
import (
"os"
"os/exec"
"github.com/junegunn/go-shellwords"
)
// ExecCommand executes the given command with $SHELL
@ -13,5 +15,14 @@ func ExecCommand(command string) *exec.Cmd {
if len(shell) == 0 {
shell = "cmd"
}
return exec.Command(shell, "/c", command)
args, _ := shellwords.Parse(command)
allArgs := make([]string, len(args)+1)
allArgs[0] = "/c"
copy(allArgs[1:], args)
return exec.Command(shell, allArgs...)
}
// IsWindows returns true on Windows
func IsWindows() bool {
return true
}

@ -1058,7 +1058,7 @@ class TestGoFZF < TestBase
def test_invalid_term
lines = `TERM=xxx #{FZF}`
assert_equal 2, $?.exitstatus
assert lines.include?('Invalid $TERM: xxx')
assert lines.include?('Invalid $TERM: xxx') || lines.include?('terminal entry not found')
end
def test_invalid_option

Loading…
Cancel
Save