mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-11-13 00:46:28 +00:00
Prepare for termbox/windows build
`TAGS=termbox make` (or `go build -tags termbox`)
This commit is contained in:
parent
2cff00dce2
commit
0c573b3dff
@ -43,7 +43,7 @@ $(SRCDIR):
|
||||
ln -s $(ROOTDIR) $(SRCDIR)
|
||||
|
||||
deps: $(SRCDIR) $(SOURCES)
|
||||
cd $(SRCDIR) && go get
|
||||
cd $(SRCDIR) && go get -tags "$(TAGS)"
|
||||
|
||||
android-build: $(SRCDIR)
|
||||
cd $(SRCDIR) && GOARCH=arm GOARM=7 CGO_ENABLED=1 go get
|
||||
|
@ -83,9 +83,11 @@ Third-party libraries used
|
||||
|
||||
- [ncurses][ncurses]
|
||||
- [mattn/go-runewidth](https://github.com/mattn/go-runewidth)
|
||||
- Licensed under [MIT](http://mattn.mit-license.org/2013)
|
||||
- Licensed under [MIT](http://mattn.mit-license.org)
|
||||
- [mattn/go-shellwords](https://github.com/mattn/go-shellwords)
|
||||
- Licensed under [MIT](http://mattn.mit-license.org/2014)
|
||||
- Licensed under [MIT](http://mattn.mit-license.org)
|
||||
- [mattn/go-isatty](https://github.com/mattn/go-isatty)
|
||||
- Licensed under [MIT](http://mattn.mit-license.org)
|
||||
|
||||
License
|
||||
-------
|
||||
|
28
src/ansi.go
28
src/ansi.go
@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/junegunn/fzf/src/curses"
|
||||
"github.com/junegunn/fzf/src/tui"
|
||||
)
|
||||
|
||||
type ansiOffset struct {
|
||||
@ -16,9 +16,9 @@ type ansiOffset struct {
|
||||
}
|
||||
|
||||
type ansiState struct {
|
||||
fg int
|
||||
bg int
|
||||
attr curses.Attr
|
||||
fg tui.Color
|
||||
bg tui.Color
|
||||
attr tui.Attr
|
||||
}
|
||||
|
||||
func (s *ansiState) colored() bool {
|
||||
@ -134,26 +134,26 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
|
||||
case 49:
|
||||
state.bg = -1
|
||||
case 1:
|
||||
state.attr = curses.Bold
|
||||
state.attr = tui.Bold
|
||||
case 2:
|
||||
state.attr = curses.Dim
|
||||
state.attr = tui.Dim
|
||||
case 4:
|
||||
state.attr = curses.Underline
|
||||
state.attr = tui.Underline
|
||||
case 5:
|
||||
state.attr = curses.Blink
|
||||
state.attr = tui.Blink
|
||||
case 7:
|
||||
state.attr = curses.Reverse
|
||||
state.attr = tui.Reverse
|
||||
case 0:
|
||||
init()
|
||||
default:
|
||||
if num >= 30 && num <= 37 {
|
||||
state.fg = num - 30
|
||||
state.fg = tui.Color(num - 30)
|
||||
} else if num >= 40 && num <= 47 {
|
||||
state.bg = num - 40
|
||||
state.bg = tui.Color(num - 40)
|
||||
} else if num >= 90 && num <= 97 {
|
||||
state.fg = num - 90 + 8
|
||||
state.fg = tui.Color(num - 90 + 8)
|
||||
} else if num >= 100 && num <= 107 {
|
||||
state.bg = num - 100 + 8
|
||||
state.bg = tui.Color(num - 100 + 8)
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
@ -164,7 +164,7 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
|
||||
state256 = 0
|
||||
}
|
||||
case 2:
|
||||
*ptr = num
|
||||
*ptr = tui.Color(num)
|
||||
state256 = 0
|
||||
}
|
||||
}
|
||||
|
@ -4,14 +4,14 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/junegunn/fzf/src/curses"
|
||||
"github.com/junegunn/fzf/src/tui"
|
||||
)
|
||||
|
||||
func TestExtractColor(t *testing.T) {
|
||||
assert := func(offset ansiOffset, b int32, e int32, fg int, bg int, bold bool) {
|
||||
var attr curses.Attr
|
||||
assert := func(offset ansiOffset, b int32, e int32, fg tui.Color, bg tui.Color, bold bool) {
|
||||
var attr tui.Attr
|
||||
if bold {
|
||||
attr = curses.Bold
|
||||
attr = tui.Bold
|
||||
}
|
||||
if offset.offset[0] != b || offset.offset[1] != e ||
|
||||
offset.color.fg != fg || offset.color.bg != bg || offset.color.attr != attr {
|
||||
|
@ -15,7 +15,6 @@ const (
|
||||
coordinatorDelayStep time.Duration = 10 * time.Millisecond
|
||||
|
||||
// Reader
|
||||
defaultCommand = `find . -path '*/\.*' -prune -o -type f -print -o -type l -print 2> /dev/null | sed s/^..//`
|
||||
readerBufferSize = 64 * 1024
|
||||
|
||||
// Terminal
|
||||
|
8
src/constants_unix.go
Normal file
8
src/constants_unix.go
Normal file
@ -0,0 +1,8 @@
|
||||
// +build !windows
|
||||
|
||||
package fzf
|
||||
|
||||
const (
|
||||
// Reader
|
||||
defaultCommand = `find . -path '*/\.*' -prune -o -type f -print -o -type l -print 2> /dev/null | sed s/^..//`
|
||||
)
|
8
src/constants_windows.go
Normal file
8
src/constants_windows.go
Normal file
@ -0,0 +1,8 @@
|
||||
// +build windows
|
||||
|
||||
package fzf
|
||||
|
||||
const (
|
||||
// Reader
|
||||
defaultCommand = `dir /s/b`
|
||||
)
|
@ -9,7 +9,7 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/junegunn/fzf/src/algo"
|
||||
"github.com/junegunn/fzf/src/curses"
|
||||
"github.com/junegunn/fzf/src/tui"
|
||||
|
||||
"github.com/junegunn/go-shellwords"
|
||||
)
|
||||
@ -142,7 +142,7 @@ type Options struct {
|
||||
Multi bool
|
||||
Ansi bool
|
||||
Mouse bool
|
||||
Theme *curses.ColorTheme
|
||||
Theme *tui.ColorTheme
|
||||
Black bool
|
||||
Reverse bool
|
||||
Cycle bool
|
||||
@ -187,7 +187,7 @@ func defaultOptions() *Options {
|
||||
Multi: false,
|
||||
Ansi: false,
|
||||
Mouse: true,
|
||||
Theme: curses.EmptyTheme(),
|
||||
Theme: tui.EmptyTheme(),
|
||||
Black: false,
|
||||
Reverse: false,
|
||||
Cycle: false,
|
||||
@ -358,60 +358,60 @@ func parseKeyChords(str string, message string) map[int]string {
|
||||
chord := 0
|
||||
switch lkey {
|
||||
case "up":
|
||||
chord = curses.Up
|
||||
chord = tui.Up
|
||||
case "down":
|
||||
chord = curses.Down
|
||||
chord = tui.Down
|
||||
case "left":
|
||||
chord = curses.Left
|
||||
chord = tui.Left
|
||||
case "right":
|
||||
chord = curses.Right
|
||||
chord = tui.Right
|
||||
case "enter", "return":
|
||||
chord = curses.CtrlM
|
||||
chord = tui.CtrlM
|
||||
case "space":
|
||||
chord = curses.AltZ + int(' ')
|
||||
chord = tui.AltZ + int(' ')
|
||||
case "bspace", "bs":
|
||||
chord = curses.BSpace
|
||||
chord = tui.BSpace
|
||||
case "alt-enter", "alt-return":
|
||||
chord = curses.AltEnter
|
||||
chord = tui.AltEnter
|
||||
case "alt-space":
|
||||
chord = curses.AltSpace
|
||||
chord = tui.AltSpace
|
||||
case "alt-/":
|
||||
chord = curses.AltSlash
|
||||
chord = tui.AltSlash
|
||||
case "alt-bs", "alt-bspace":
|
||||
chord = curses.AltBS
|
||||
chord = tui.AltBS
|
||||
case "tab":
|
||||
chord = curses.Tab
|
||||
chord = tui.Tab
|
||||
case "btab", "shift-tab":
|
||||
chord = curses.BTab
|
||||
chord = tui.BTab
|
||||
case "esc":
|
||||
chord = curses.ESC
|
||||
chord = tui.ESC
|
||||
case "del":
|
||||
chord = curses.Del
|
||||
chord = tui.Del
|
||||
case "home":
|
||||
chord = curses.Home
|
||||
chord = tui.Home
|
||||
case "end":
|
||||
chord = curses.End
|
||||
chord = tui.End
|
||||
case "pgup", "page-up":
|
||||
chord = curses.PgUp
|
||||
chord = tui.PgUp
|
||||
case "pgdn", "page-down":
|
||||
chord = curses.PgDn
|
||||
chord = tui.PgDn
|
||||
case "shift-left":
|
||||
chord = curses.SLeft
|
||||
chord = tui.SLeft
|
||||
case "shift-right":
|
||||
chord = curses.SRight
|
||||
chord = tui.SRight
|
||||
case "double-click":
|
||||
chord = curses.DoubleClick
|
||||
chord = tui.DoubleClick
|
||||
case "f10":
|
||||
chord = curses.F10
|
||||
chord = tui.F10
|
||||
default:
|
||||
if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
|
||||
chord = curses.CtrlA + int(lkey[5]) - 'a'
|
||||
chord = tui.CtrlA + int(lkey[5]) - 'a'
|
||||
} else if len(key) == 5 && strings.HasPrefix(lkey, "alt-") && isAlphabet(lkey[4]) {
|
||||
chord = curses.AltA + int(lkey[4]) - 'a'
|
||||
chord = tui.AltA + int(lkey[4]) - 'a'
|
||||
} else if len(key) == 2 && strings.HasPrefix(lkey, "f") && key[1] >= '1' && key[1] <= '9' {
|
||||
chord = curses.F1 + int(key[1]) - '1'
|
||||
chord = tui.F1 + int(key[1]) - '1'
|
||||
} else if utf8.RuneCountInString(key) == 1 {
|
||||
chord = curses.AltZ + int([]rune(key)[0])
|
||||
chord = tui.AltZ + int([]rune(key)[0])
|
||||
} else {
|
||||
errorExit("unsupported key: " + key)
|
||||
}
|
||||
@ -458,7 +458,7 @@ func parseTiebreak(str string) []criterion {
|
||||
return criteria
|
||||
}
|
||||
|
||||
func dupeTheme(theme *curses.ColorTheme) *curses.ColorTheme {
|
||||
func dupeTheme(theme *tui.ColorTheme) *tui.ColorTheme {
|
||||
if theme != nil {
|
||||
dupe := *theme
|
||||
return &dupe
|
||||
@ -466,16 +466,16 @@ func dupeTheme(theme *curses.ColorTheme) *curses.ColorTheme {
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseTheme(defaultTheme *curses.ColorTheme, str string) *curses.ColorTheme {
|
||||
func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
||||
theme := dupeTheme(defaultTheme)
|
||||
for _, str := range strings.Split(strings.ToLower(str), ",") {
|
||||
switch str {
|
||||
case "dark":
|
||||
theme = dupeTheme(curses.Dark256)
|
||||
theme = dupeTheme(tui.Dark256)
|
||||
case "light":
|
||||
theme = dupeTheme(curses.Light256)
|
||||
theme = dupeTheme(tui.Light256)
|
||||
case "16":
|
||||
theme = dupeTheme(curses.Default16)
|
||||
theme = dupeTheme(tui.Default16)
|
||||
case "bw", "no":
|
||||
theme = nil
|
||||
default:
|
||||
@ -495,7 +495,7 @@ func parseTheme(defaultTheme *curses.ColorTheme, str string) *curses.ColorTheme
|
||||
if err != nil || ansi32 < -1 || ansi32 > 255 {
|
||||
fail()
|
||||
}
|
||||
ansi := int16(ansi32)
|
||||
ansi := tui.Color(ansi32)
|
||||
switch pair[0] {
|
||||
case "fg":
|
||||
theme.Fg = ansi
|
||||
@ -572,9 +572,9 @@ func parseKeymap(keymap map[int]actionType, execmap map[int]string, str string)
|
||||
}
|
||||
var key int
|
||||
if len(pair[0]) == 1 && pair[0][0] == escapedColon {
|
||||
key = ':' + curses.AltZ
|
||||
key = ':' + tui.AltZ
|
||||
} else if len(pair[0]) == 1 && pair[0][0] == escapedComma {
|
||||
key = ',' + curses.AltZ
|
||||
key = ',' + tui.AltZ
|
||||
} else {
|
||||
keys := parseKeyChords(pair[0], "key name required")
|
||||
key = firstKey(keys)
|
||||
@ -868,7 +868,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
||||
case "--color":
|
||||
spec := optionalNextString(allArgs, &i)
|
||||
if len(spec) == 0 {
|
||||
opts.Theme = curses.EmptyTheme()
|
||||
opts.Theme = tui.EmptyTheme()
|
||||
} else {
|
||||
opts.Theme = parseTheme(opts.Theme, spec)
|
||||
}
|
||||
@ -905,7 +905,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
||||
case "+c", "--no-color":
|
||||
opts.Theme = nil
|
||||
case "+2", "--no-256":
|
||||
opts.Theme = curses.Default16
|
||||
opts.Theme = tui.Default16
|
||||
case "--black":
|
||||
opts.Black = true
|
||||
case "--no-black":
|
||||
@ -1071,11 +1071,11 @@ func parseOptions(opts *Options, allArgs []string) {
|
||||
func postProcessOptions(opts *Options) {
|
||||
// Default actions for CTRL-N / CTRL-P when --history is set
|
||||
if opts.History != nil {
|
||||
if _, prs := opts.Keymap[curses.CtrlP]; !prs {
|
||||
opts.Keymap[curses.CtrlP] = actPreviousHistory
|
||||
if _, prs := opts.Keymap[tui.CtrlP]; !prs {
|
||||
opts.Keymap[tui.CtrlP] = actPreviousHistory
|
||||
}
|
||||
if _, prs := opts.Keymap[curses.CtrlN]; !prs {
|
||||
opts.Keymap[curses.CtrlN] = actNextHistory
|
||||
if _, prs := opts.Keymap[tui.CtrlN]; !prs {
|
||||
opts.Keymap[tui.CtrlN] = actNextHistory
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/junegunn/fzf/src/curses"
|
||||
"github.com/junegunn/fzf/src/tui"
|
||||
"github.com/junegunn/fzf/src/util"
|
||||
)
|
||||
|
||||
@ -133,48 +133,48 @@ func TestParseKeys(t *testing.T) {
|
||||
if len(pairs) != 11 {
|
||||
t.Error(11)
|
||||
}
|
||||
check(curses.CtrlZ, "ctrl-z")
|
||||
check(curses.AltZ, "alt-z")
|
||||
check(curses.F2, "f2")
|
||||
check(curses.AltZ+'@', "@")
|
||||
check(curses.AltA, "Alt-a")
|
||||
check(curses.AltZ+'!', "!")
|
||||
check(curses.CtrlA+'g'-'a', "ctrl-G")
|
||||
check(curses.AltZ+'J', "J")
|
||||
check(curses.AltZ+'g', "g")
|
||||
check(curses.AltEnter, "ALT-enter")
|
||||
check(curses.AltSpace, "alt-SPACE")
|
||||
check(tui.CtrlZ, "ctrl-z")
|
||||
check(tui.AltZ, "alt-z")
|
||||
check(tui.F2, "f2")
|
||||
check(tui.AltZ+'@', "@")
|
||||
check(tui.AltA, "Alt-a")
|
||||
check(tui.AltZ+'!', "!")
|
||||
check(tui.CtrlA+'g'-'a', "ctrl-G")
|
||||
check(tui.AltZ+'J', "J")
|
||||
check(tui.AltZ+'g', "g")
|
||||
check(tui.AltEnter, "ALT-enter")
|
||||
check(tui.AltSpace, "alt-SPACE")
|
||||
|
||||
// Synonyms
|
||||
pairs = parseKeyChords("enter,Return,space,tab,btab,esc,up,down,left,right", "")
|
||||
if len(pairs) != 9 {
|
||||
t.Error(9)
|
||||
}
|
||||
check(curses.CtrlM, "Return")
|
||||
check(curses.AltZ+' ', "space")
|
||||
check(curses.Tab, "tab")
|
||||
check(curses.BTab, "btab")
|
||||
check(curses.ESC, "esc")
|
||||
check(curses.Up, "up")
|
||||
check(curses.Down, "down")
|
||||
check(curses.Left, "left")
|
||||
check(curses.Right, "right")
|
||||
check(tui.CtrlM, "Return")
|
||||
check(tui.AltZ+' ', "space")
|
||||
check(tui.Tab, "tab")
|
||||
check(tui.BTab, "btab")
|
||||
check(tui.ESC, "esc")
|
||||
check(tui.Up, "up")
|
||||
check(tui.Down, "down")
|
||||
check(tui.Left, "left")
|
||||
check(tui.Right, "right")
|
||||
|
||||
pairs = parseKeyChords("Tab,Ctrl-I,PgUp,page-up,pgdn,Page-Down,Home,End,Alt-BS,Alt-BSpace,shift-left,shift-right,btab,shift-tab,return,Enter,bspace", "")
|
||||
if len(pairs) != 11 {
|
||||
t.Error(11)
|
||||
}
|
||||
check(curses.Tab, "Ctrl-I")
|
||||
check(curses.PgUp, "page-up")
|
||||
check(curses.PgDn, "Page-Down")
|
||||
check(curses.Home, "Home")
|
||||
check(curses.End, "End")
|
||||
check(curses.AltBS, "Alt-BSpace")
|
||||
check(curses.SLeft, "shift-left")
|
||||
check(curses.SRight, "shift-right")
|
||||
check(curses.BTab, "shift-tab")
|
||||
check(curses.CtrlM, "Enter")
|
||||
check(curses.BSpace, "bspace")
|
||||
check(tui.Tab, "Ctrl-I")
|
||||
check(tui.PgUp, "page-up")
|
||||
check(tui.PgDn, "Page-Down")
|
||||
check(tui.Home, "Home")
|
||||
check(tui.End, "End")
|
||||
check(tui.AltBS, "Alt-BSpace")
|
||||
check(tui.SLeft, "shift-left")
|
||||
check(tui.SRight, "shift-right")
|
||||
check(tui.BTab, "shift-tab")
|
||||
check(tui.CtrlM, "Enter")
|
||||
check(tui.BSpace, "bspace")
|
||||
}
|
||||
|
||||
func TestParseKeysWithComma(t *testing.T) {
|
||||
@ -191,36 +191,36 @@ func TestParseKeysWithComma(t *testing.T) {
|
||||
|
||||
pairs := parseKeyChords(",", "")
|
||||
checkN(len(pairs), 1)
|
||||
check(pairs, curses.AltZ+',', ",")
|
||||
check(pairs, tui.AltZ+',', ",")
|
||||
|
||||
pairs = parseKeyChords(",,a,b", "")
|
||||
checkN(len(pairs), 3)
|
||||
check(pairs, curses.AltZ+'a', "a")
|
||||
check(pairs, curses.AltZ+'b', "b")
|
||||
check(pairs, curses.AltZ+',', ",")
|
||||
check(pairs, tui.AltZ+'a', "a")
|
||||
check(pairs, tui.AltZ+'b', "b")
|
||||
check(pairs, tui.AltZ+',', ",")
|
||||
|
||||
pairs = parseKeyChords("a,b,,", "")
|
||||
checkN(len(pairs), 3)
|
||||
check(pairs, curses.AltZ+'a', "a")
|
||||
check(pairs, curses.AltZ+'b', "b")
|
||||
check(pairs, curses.AltZ+',', ",")
|
||||
check(pairs, tui.AltZ+'a', "a")
|
||||
check(pairs, tui.AltZ+'b', "b")
|
||||
check(pairs, tui.AltZ+',', ",")
|
||||
|
||||
pairs = parseKeyChords("a,,,b", "")
|
||||
checkN(len(pairs), 3)
|
||||
check(pairs, curses.AltZ+'a', "a")
|
||||
check(pairs, curses.AltZ+'b', "b")
|
||||
check(pairs, curses.AltZ+',', ",")
|
||||
check(pairs, tui.AltZ+'a', "a")
|
||||
check(pairs, tui.AltZ+'b', "b")
|
||||
check(pairs, tui.AltZ+',', ",")
|
||||
|
||||
pairs = parseKeyChords("a,,,b,c", "")
|
||||
checkN(len(pairs), 4)
|
||||
check(pairs, curses.AltZ+'a', "a")
|
||||
check(pairs, curses.AltZ+'b', "b")
|
||||
check(pairs, curses.AltZ+'c', "c")
|
||||
check(pairs, curses.AltZ+',', ",")
|
||||
check(pairs, tui.AltZ+'a', "a")
|
||||
check(pairs, tui.AltZ+'b', "b")
|
||||
check(pairs, tui.AltZ+'c', "c")
|
||||
check(pairs, tui.AltZ+',', ",")
|
||||
|
||||
pairs = parseKeyChords(",,,", "")
|
||||
checkN(len(pairs), 1)
|
||||
check(pairs, curses.AltZ+',', ",")
|
||||
check(pairs, tui.AltZ+',', ",")
|
||||
}
|
||||
|
||||
func TestBind(t *testing.T) {
|
||||
@ -236,41 +236,41 @@ func TestBind(t *testing.T) {
|
||||
}
|
||||
keymap := defaultKeymap()
|
||||
execmap := make(map[int]string)
|
||||
check(actBeginningOfLine, keymap[curses.CtrlA])
|
||||
check(actBeginningOfLine, keymap[tui.CtrlA])
|
||||
parseKeymap(keymap, execmap,
|
||||
"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)")
|
||||
check(actKillLine, keymap[curses.CtrlA])
|
||||
check(actToggleSort, keymap[curses.CtrlB])
|
||||
check(actPageUp, keymap[curses.AltZ+'c'])
|
||||
check(actAbort, keymap[curses.AltZ+','])
|
||||
check(actAccept, keymap[curses.AltZ+':'])
|
||||
check(actPageDown, keymap[curses.AltZ])
|
||||
check(actExecute, keymap[curses.F1])
|
||||
check(actExecute, keymap[curses.F2])
|
||||
check(actExecute, keymap[curses.F3])
|
||||
check(actExecute, keymap[curses.F4])
|
||||
checkString("ls {}", execmap[curses.F1])
|
||||
checkString("echo {}, {}, {}", execmap[curses.F2])
|
||||
checkString("echo '({})'", execmap[curses.F3])
|
||||
checkString("less {}", execmap[curses.F4])
|
||||
checkString("echo (,),[,],/,:,;,%,{}", execmap[curses.AltA])
|
||||
checkString("echo (,),[,],/,:,@,%,{}", execmap[curses.AltB])
|
||||
checkString("\nfoobar,Y:execute(baz)", execmap[curses.AltZ+'X'])
|
||||
check(actKillLine, keymap[tui.CtrlA])
|
||||
check(actToggleSort, keymap[tui.CtrlB])
|
||||
check(actPageUp, keymap[tui.AltZ+'c'])
|
||||
check(actAbort, keymap[tui.AltZ+','])
|
||||
check(actAccept, keymap[tui.AltZ+':'])
|
||||
check(actPageDown, keymap[tui.AltZ])
|
||||
check(actExecute, keymap[tui.F1])
|
||||
check(actExecute, keymap[tui.F2])
|
||||
check(actExecute, keymap[tui.F3])
|
||||
check(actExecute, keymap[tui.F4])
|
||||
checkString("ls {}", execmap[tui.F1])
|
||||
checkString("echo {}, {}, {}", execmap[tui.F2])
|
||||
checkString("echo '({})'", execmap[tui.F3])
|
||||
checkString("less {}", execmap[tui.F4])
|
||||
checkString("echo (,),[,],/,:,;,%,{}", execmap[tui.AltA])
|
||||
checkString("echo (,),[,],/,:,@,%,{}", execmap[tui.AltB])
|
||||
checkString("\nfoobar,Y:execute(baz)", execmap[tui.AltZ+'X'])
|
||||
|
||||
for idx, char := range []rune{'~', '!', '@', '#', '$', '%', '^', '&', '*', '|', ';', '/'} {
|
||||
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])])
|
||||
checkString("foobar", execmap[tui.AltZ+int([]rune(fmt.Sprintf("%d", idx%10))[0])])
|
||||
}
|
||||
|
||||
parseKeymap(keymap, execmap, "f1:abort")
|
||||
check(actAbort, keymap[curses.F1])
|
||||
check(actAbort, keymap[tui.F1])
|
||||
}
|
||||
|
||||
func TestColorSpec(t *testing.T) {
|
||||
theme := curses.Dark256
|
||||
theme := tui.Dark256
|
||||
dark := parseTheme(theme, "dark")
|
||||
if *dark != *theme {
|
||||
t.Errorf("colors should be equivalent")
|
||||
@ -283,7 +283,7 @@ func TestColorSpec(t *testing.T) {
|
||||
if *light == *theme {
|
||||
t.Errorf("should not be equivalent")
|
||||
}
|
||||
if *light != *curses.Light256 {
|
||||
if *light != *tui.Light256 {
|
||||
t.Errorf("colors should be equivalent")
|
||||
}
|
||||
if light == theme {
|
||||
@ -294,23 +294,23 @@ func TestColorSpec(t *testing.T) {
|
||||
if customized.Fg != 231 || customized.Bg != 232 {
|
||||
t.Errorf("color not customized")
|
||||
}
|
||||
if *curses.Dark256 == *customized {
|
||||
if *tui.Dark256 == *customized {
|
||||
t.Errorf("colors should not be equivalent")
|
||||
}
|
||||
customized.Fg = curses.Dark256.Fg
|
||||
customized.Bg = curses.Dark256.Bg
|
||||
if *curses.Dark256 != *customized {
|
||||
t.Errorf("colors should now be equivalent: %v, %v", curses.Dark256, customized)
|
||||
customized.Fg = tui.Dark256.Fg
|
||||
customized.Bg = tui.Dark256.Bg
|
||||
if *tui.Dark256 != *customized {
|
||||
t.Errorf("colors should now be equivalent: %v, %v", tui.Dark256, customized)
|
||||
}
|
||||
|
||||
customized = parseTheme(theme, "fg:231,dark,bg:232")
|
||||
if customized.Fg != curses.Dark256.Fg || customized.Bg == curses.Dark256.Bg {
|
||||
if customized.Fg != tui.Dark256.Fg || customized.Bg == tui.Dark256.Bg {
|
||||
t.Errorf("color not customized")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNilTheme(t *testing.T) {
|
||||
var theme *curses.ColorTheme
|
||||
var theme *tui.ColorTheme
|
||||
newTheme := parseTheme(theme, "prompt:12")
|
||||
if newTheme != nil {
|
||||
t.Errorf("color is disabled. keep it that way.")
|
||||
@ -330,21 +330,21 @@ func TestDefaultCtrlNP(t *testing.T) {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
check([]string{}, curses.CtrlN, actDown)
|
||||
check([]string{}, curses.CtrlP, actUp)
|
||||
check([]string{}, tui.CtrlN, actDown)
|
||||
check([]string{}, tui.CtrlP, actUp)
|
||||
|
||||
check([]string{"--bind=ctrl-n:accept"}, curses.CtrlN, actAccept)
|
||||
check([]string{"--bind=ctrl-p:accept"}, curses.CtrlP, actAccept)
|
||||
check([]string{"--bind=ctrl-n:accept"}, tui.CtrlN, actAccept)
|
||||
check([]string{"--bind=ctrl-p:accept"}, tui.CtrlP, actAccept)
|
||||
|
||||
hist := "--history=/tmp/fzf-history"
|
||||
check([]string{hist}, curses.CtrlN, actNextHistory)
|
||||
check([]string{hist}, curses.CtrlP, actPreviousHistory)
|
||||
check([]string{hist}, tui.CtrlN, actNextHistory)
|
||||
check([]string{hist}, tui.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-n:accept"}, tui.CtrlN, actAccept)
|
||||
check([]string{hist, "--bind=ctrl-n:accept"}, tui.CtrlP, actPreviousHistory)
|
||||
|
||||
check([]string{hist, "--bind=ctrl-p:accept"}, curses.CtrlN, actNextHistory)
|
||||
check([]string{hist, "--bind=ctrl-p:accept"}, curses.CtrlP, actAccept)
|
||||
check([]string{hist, "--bind=ctrl-p:accept"}, tui.CtrlN, actNextHistory)
|
||||
check([]string{hist, "--bind=ctrl-p:accept"}, tui.CtrlP, actAccept)
|
||||
}
|
||||
|
||||
func optsFor(words ...string) *Options {
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"sort"
|
||||
"unicode"
|
||||
|
||||
"github.com/junegunn/fzf/src/curses"
|
||||
"github.com/junegunn/fzf/src/tui"
|
||||
"github.com/junegunn/fzf/src/util"
|
||||
)
|
||||
|
||||
@ -14,8 +14,8 @@ type Offset [2]int32
|
||||
|
||||
type colorOffset struct {
|
||||
offset [2]int32
|
||||
color int
|
||||
attr curses.Attr
|
||||
color tui.ColorPair
|
||||
attr tui.Attr
|
||||
index int32
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ func minRank() rank {
|
||||
return rank{index: 0, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
|
||||
}
|
||||
|
||||
func (result *Result) colorOffsets(matchOffsets []Offset, theme *curses.ColorTheme, color int, attr curses.Attr, current bool) []colorOffset {
|
||||
func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme, color tui.ColorPair, attr tui.Attr, current bool) []colorOffset {
|
||||
itemColors := result.item.Colors()
|
||||
|
||||
// No ANSI code, or --color=no
|
||||
@ -149,23 +149,23 @@ func (result *Result) colorOffsets(matchOffsets []Offset, theme *curses.ColorThe
|
||||
fg := ansi.color.fg
|
||||
if fg == -1 {
|
||||
if current {
|
||||
fg = int(theme.Current)
|
||||
fg = theme.Current
|
||||
} else {
|
||||
fg = int(theme.Fg)
|
||||
fg = theme.Fg
|
||||
}
|
||||
}
|
||||
bg := ansi.color.bg
|
||||
if bg == -1 {
|
||||
if current {
|
||||
bg = int(theme.DarkBg)
|
||||
bg = theme.DarkBg
|
||||
} else {
|
||||
bg = int(theme.Bg)
|
||||
bg = theme.Bg
|
||||
}
|
||||
}
|
||||
colors = append(colors, colorOffset{
|
||||
offset: [2]int32{int32(start), int32(idx)},
|
||||
color: curses.PairFor(fg, bg),
|
||||
attr: ansi.color.attr | attr})
|
||||
color: tui.PairFor(fg, bg),
|
||||
attr: ansi.color.attr.Merge(attr)})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
// +build !termbox
|
||||
|
||||
package fzf
|
||||
|
||||
import (
|
||||
@ -5,7 +7,7 @@ import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/junegunn/fzf/src/curses"
|
||||
"github.com/junegunn/fzf/src/tui"
|
||||
"github.com/junegunn/fzf/src/util"
|
||||
)
|
||||
|
||||
@ -98,26 +100,26 @@ func TestColorOffset(t *testing.T) {
|
||||
item: &Item{
|
||||
colors: &[]ansiOffset{
|
||||
ansiOffset{[2]int32{0, 20}, ansiState{1, 5, 0}},
|
||||
ansiOffset{[2]int32{22, 27}, ansiState{2, 6, curses.Bold}},
|
||||
ansiOffset{[2]int32{22, 27}, ansiState{2, 6, tui.Bold}},
|
||||
ansiOffset{[2]int32{30, 32}, ansiState{3, 7, 0}},
|
||||
ansiOffset{[2]int32{33, 40}, ansiState{4, 8, curses.Bold}}}}}
|
||||
ansiOffset{[2]int32{33, 40}, ansiState{4, 8, tui.Bold}}}}}
|
||||
// [{[0 5] 9 false} {[5 15] 99 false} {[15 20] 9 false} {[22 25] 10 true} {[25 35] 99 false} {[35 40] 11 true}]
|
||||
|
||||
colors := item.colorOffsets(offsets, curses.Dark256, 99, 0, true)
|
||||
assert := func(idx int, b int32, e int32, c int, bold bool) {
|
||||
var attr curses.Attr
|
||||
colors := item.colorOffsets(offsets, tui.Dark256, 99, 0, true)
|
||||
assert := func(idx int, b int32, e int32, c tui.ColorPair, bold bool) {
|
||||
var attr tui.Attr
|
||||
if bold {
|
||||
attr = curses.Bold
|
||||
attr = tui.Bold
|
||||
}
|
||||
o := colors[idx]
|
||||
if o.offset[0] != b || o.offset[1] != e || o.color != c || o.attr != attr {
|
||||
t.Error(o)
|
||||
}
|
||||
}
|
||||
assert(0, 0, 5, curses.ColUser, false)
|
||||
assert(0, 0, 5, tui.ColUser, false)
|
||||
assert(1, 5, 15, 99, false)
|
||||
assert(2, 15, 20, curses.ColUser, false)
|
||||
assert(3, 22, 25, curses.ColUser+1, true)
|
||||
assert(2, 15, 20, tui.ColUser, false)
|
||||
assert(3, 22, 25, tui.ColUser+1, true)
|
||||
assert(4, 25, 35, 99, false)
|
||||
assert(5, 35, 40, curses.ColUser+2, true)
|
||||
assert(5, 35, 40, tui.ColUser+2, true)
|
||||
}
|
||||
|
181
src/terminal.go
181
src/terminal.go
@ -12,7 +12,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
C "github.com/junegunn/fzf/src/curses"
|
||||
"github.com/junegunn/fzf/src/tui"
|
||||
"github.com/junegunn/fzf/src/util"
|
||||
|
||||
"github.com/junegunn/go-runewidth"
|
||||
@ -69,9 +69,9 @@ type Terminal struct {
|
||||
header0 []string
|
||||
ansi bool
|
||||
margin [4]sizeSpec
|
||||
window *C.Window
|
||||
bwindow *C.Window
|
||||
pwindow *C.Window
|
||||
window *tui.Window
|
||||
bwindow *tui.Window
|
||||
pwindow *tui.Window
|
||||
count int
|
||||
progress int
|
||||
reading bool
|
||||
@ -90,7 +90,7 @@ type Terminal struct {
|
||||
suppress bool
|
||||
startChan chan bool
|
||||
slab *util.Slab
|
||||
theme *C.ColorTheme
|
||||
theme *tui.ColorTheme
|
||||
}
|
||||
|
||||
type selectedItem struct {
|
||||
@ -187,51 +187,51 @@ const (
|
||||
|
||||
func defaultKeymap() map[int]actionType {
|
||||
keymap := make(map[int]actionType)
|
||||
keymap[C.Invalid] = actInvalid
|
||||
keymap[C.CtrlA] = actBeginningOfLine
|
||||
keymap[C.CtrlB] = actBackwardChar
|
||||
keymap[C.CtrlC] = actAbort
|
||||
keymap[C.CtrlG] = actAbort
|
||||
keymap[C.CtrlQ] = actAbort
|
||||
keymap[C.ESC] = actAbort
|
||||
keymap[C.CtrlD] = actDeleteCharEOF
|
||||
keymap[C.CtrlE] = actEndOfLine
|
||||
keymap[C.CtrlF] = actForwardChar
|
||||
keymap[C.CtrlH] = actBackwardDeleteChar
|
||||
keymap[C.BSpace] = actBackwardDeleteChar
|
||||
keymap[C.Tab] = actToggleDown
|
||||
keymap[C.BTab] = actToggleUp
|
||||
keymap[C.CtrlJ] = actDown
|
||||
keymap[C.CtrlK] = actUp
|
||||
keymap[C.CtrlL] = actClearScreen
|
||||
keymap[C.CtrlM] = actAccept
|
||||
keymap[C.CtrlN] = actDown
|
||||
keymap[C.CtrlP] = actUp
|
||||
keymap[C.CtrlU] = actUnixLineDiscard
|
||||
keymap[C.CtrlW] = actUnixWordRubout
|
||||
keymap[C.CtrlY] = actYank
|
||||
keymap[tui.Invalid] = actInvalid
|
||||
keymap[tui.CtrlA] = actBeginningOfLine
|
||||
keymap[tui.CtrlB] = actBackwardChar
|
||||
keymap[tui.CtrlC] = actAbort
|
||||
keymap[tui.CtrlG] = actAbort
|
||||
keymap[tui.CtrlQ] = actAbort
|
||||
keymap[tui.ESC] = actAbort
|
||||
keymap[tui.CtrlD] = actDeleteCharEOF
|
||||
keymap[tui.CtrlE] = actEndOfLine
|
||||
keymap[tui.CtrlF] = actForwardChar
|
||||
keymap[tui.CtrlH] = actBackwardDeleteChar
|
||||
keymap[tui.BSpace] = actBackwardDeleteChar
|
||||
keymap[tui.Tab] = actToggleDown
|
||||
keymap[tui.BTab] = actToggleUp
|
||||
keymap[tui.CtrlJ] = actDown
|
||||
keymap[tui.CtrlK] = actUp
|
||||
keymap[tui.CtrlL] = actClearScreen
|
||||
keymap[tui.CtrlM] = actAccept
|
||||
keymap[tui.CtrlN] = actDown
|
||||
keymap[tui.CtrlP] = actUp
|
||||
keymap[tui.CtrlU] = actUnixLineDiscard
|
||||
keymap[tui.CtrlW] = actUnixWordRubout
|
||||
keymap[tui.CtrlY] = actYank
|
||||
|
||||
keymap[C.AltB] = actBackwardWord
|
||||
keymap[C.SLeft] = actBackwardWord
|
||||
keymap[C.AltF] = actForwardWord
|
||||
keymap[C.SRight] = actForwardWord
|
||||
keymap[C.AltD] = actKillWord
|
||||
keymap[C.AltBS] = actBackwardKillWord
|
||||
keymap[tui.AltB] = actBackwardWord
|
||||
keymap[tui.SLeft] = actBackwardWord
|
||||
keymap[tui.AltF] = actForwardWord
|
||||
keymap[tui.SRight] = actForwardWord
|
||||
keymap[tui.AltD] = actKillWord
|
||||
keymap[tui.AltBS] = actBackwardKillWord
|
||||
|
||||
keymap[C.Up] = actUp
|
||||
keymap[C.Down] = actDown
|
||||
keymap[C.Left] = actBackwardChar
|
||||
keymap[C.Right] = actForwardChar
|
||||
keymap[tui.Up] = actUp
|
||||
keymap[tui.Down] = actDown
|
||||
keymap[tui.Left] = actBackwardChar
|
||||
keymap[tui.Right] = actForwardChar
|
||||
|
||||
keymap[C.Home] = actBeginningOfLine
|
||||
keymap[C.End] = actEndOfLine
|
||||
keymap[C.Del] = actDeleteChar
|
||||
keymap[C.PgUp] = actPageUp
|
||||
keymap[C.PgDn] = actPageDown
|
||||
keymap[tui.Home] = actBeginningOfLine
|
||||
keymap[tui.End] = actEndOfLine
|
||||
keymap[tui.Del] = actDeleteChar
|
||||
keymap[tui.PgUp] = actPageUp
|
||||
keymap[tui.PgDn] = actPageDown
|
||||
|
||||
keymap[C.Rune] = actRune
|
||||
keymap[C.Mouse] = actMouse
|
||||
keymap[C.DoubleClick] = actAccept
|
||||
keymap[tui.Rune] = actRune
|
||||
keymap[tui.Mouse] = actMouse
|
||||
keymap[tui.DoubleClick] = actAccept
|
||||
return keymap
|
||||
}
|
||||
|
||||
@ -299,7 +299,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
theme: opts.Theme,
|
||||
startChan: make(chan bool, 1),
|
||||
initFunc: func() {
|
||||
C.Init(opts.Theme, opts.Black, opts.Mouse)
|
||||
tui.Init(opts.Theme, opts.Black, opts.Mouse)
|
||||
}}
|
||||
}
|
||||
|
||||
@ -429,8 +429,8 @@ func calculateSize(base int, size sizeSpec, margin int, minSize int) int {
|
||||
}
|
||||
|
||||
func (t *Terminal) resizeWindows() {
|
||||
screenWidth := C.MaxX()
|
||||
screenHeight := C.MaxY()
|
||||
screenWidth := tui.MaxX()
|
||||
screenHeight := tui.MaxY()
|
||||
marginInt := [4]int{}
|
||||
for idx, sizeSpec := range t.margin {
|
||||
if sizeSpec.percent {
|
||||
@ -479,33 +479,33 @@ func (t *Terminal) resizeWindows() {
|
||||
height := screenHeight - marginInt[0] - marginInt[2]
|
||||
if t.isPreviewEnabled() {
|
||||
createPreviewWindow := func(y int, x int, w int, h int) {
|
||||
t.bwindow = C.NewWindow(y, x, w, h, true)
|
||||
t.pwindow = C.NewWindow(y+1, x+2, w-4, h-2, false)
|
||||
t.bwindow = tui.NewWindow(y, x, w, h, true)
|
||||
t.pwindow = tui.NewWindow(y+1, x+2, w-4, h-2, false)
|
||||
}
|
||||
switch t.preview.position {
|
||||
case posUp:
|
||||
pheight := calculateSize(height, t.preview.size, minHeight, 3)
|
||||
t.window = C.NewWindow(
|
||||
t.window = tui.NewWindow(
|
||||
marginInt[0]+pheight, marginInt[3], width, height-pheight, false)
|
||||
createPreviewWindow(marginInt[0], marginInt[3], width, pheight)
|
||||
case posDown:
|
||||
pheight := calculateSize(height, t.preview.size, minHeight, 3)
|
||||
t.window = C.NewWindow(
|
||||
t.window = tui.NewWindow(
|
||||
marginInt[0], marginInt[3], width, height-pheight, false)
|
||||
createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
|
||||
case posLeft:
|
||||
pwidth := calculateSize(width, t.preview.size, minWidth, 5)
|
||||
t.window = C.NewWindow(
|
||||
t.window = tui.NewWindow(
|
||||
marginInt[0], marginInt[3]+pwidth, width-pwidth, height, false)
|
||||
createPreviewWindow(marginInt[0], marginInt[3], pwidth, height)
|
||||
case posRight:
|
||||
pwidth := calculateSize(width, t.preview.size, minWidth, 5)
|
||||
t.window = C.NewWindow(
|
||||
t.window = tui.NewWindow(
|
||||
marginInt[0], marginInt[3], width-pwidth, height, false)
|
||||
createPreviewWindow(marginInt[0], marginInt[3]+width-pwidth, pwidth, height)
|
||||
}
|
||||
} else {
|
||||
t.window = C.NewWindow(
|
||||
t.window = tui.NewWindow(
|
||||
marginInt[0],
|
||||
marginInt[3],
|
||||
width,
|
||||
@ -531,24 +531,24 @@ func (t *Terminal) placeCursor() {
|
||||
|
||||
func (t *Terminal) printPrompt() {
|
||||
t.move(0, 0, true)
|
||||
t.window.CPrint(C.ColPrompt, C.Bold, t.prompt)
|
||||
t.window.CPrint(C.ColNormal, C.Bold, string(t.input))
|
||||
t.window.CPrint(tui.ColPrompt, tui.Bold, t.prompt)
|
||||
t.window.CPrint(tui.ColNormal, tui.Bold, string(t.input))
|
||||
}
|
||||
|
||||
func (t *Terminal) printInfo() {
|
||||
if t.inlineInfo {
|
||||
t.move(0, displayWidth([]rune(t.prompt))+displayWidth(t.input)+1, true)
|
||||
if t.reading {
|
||||
t.window.CPrint(C.ColSpinner, C.Bold, " < ")
|
||||
t.window.CPrint(tui.ColSpinner, tui.Bold, " < ")
|
||||
} else {
|
||||
t.window.CPrint(C.ColPrompt, C.Bold, " < ")
|
||||
t.window.CPrint(tui.ColPrompt, tui.Bold, " < ")
|
||||
}
|
||||
} else {
|
||||
t.move(1, 0, true)
|
||||
if t.reading {
|
||||
duration := int64(spinnerDuration)
|
||||
idx := (time.Now().UnixNano() % (duration * int64(len(_spinner)))) / duration
|
||||
t.window.CPrint(C.ColSpinner, C.Bold, _spinner[idx])
|
||||
t.window.CPrint(tui.ColSpinner, tui.Bold, _spinner[idx])
|
||||
}
|
||||
t.move(1, 2, false)
|
||||
}
|
||||
@ -567,7 +567,7 @@ func (t *Terminal) printInfo() {
|
||||
if t.progress > 0 && t.progress < 100 {
|
||||
output += fmt.Sprintf(" (%d%%)", t.progress)
|
||||
}
|
||||
t.window.CPrint(C.ColInfo, 0, output)
|
||||
t.window.CPrint(tui.ColInfo, 0, output)
|
||||
}
|
||||
|
||||
func (t *Terminal) printHeader() {
|
||||
@ -591,7 +591,8 @@ func (t *Terminal) printHeader() {
|
||||
colors: colors}
|
||||
|
||||
t.move(line, 2, true)
|
||||
t.printHighlighted(&Result{item: item}, 0, C.ColHeader, 0, false)
|
||||
t.printHighlighted(&Result{item: item},
|
||||
tui.AttrRegular, tui.ColHeader, tui.ColDefault, false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -625,21 +626,21 @@ func (t *Terminal) printItem(result *Result, i int, current bool) {
|
||||
} else if current {
|
||||
label = ">"
|
||||
}
|
||||
t.window.CPrint(C.ColCursor, C.Bold, label)
|
||||
t.window.CPrint(tui.ColCursor, tui.Bold, label)
|
||||
if current {
|
||||
if selected {
|
||||
t.window.CPrint(C.ColSelected, C.Bold, ">")
|
||||
t.window.CPrint(tui.ColSelected, tui.Bold, ">")
|
||||
} else {
|
||||
t.window.CPrint(C.ColCurrent, C.Bold, " ")
|
||||
t.window.CPrint(tui.ColCurrent, tui.Bold, " ")
|
||||
}
|
||||
t.printHighlighted(result, C.Bold, C.ColCurrent, C.ColCurrentMatch, true)
|
||||
t.printHighlighted(result, tui.Bold, tui.ColCurrent, tui.ColCurrentMatch, true)
|
||||
} else {
|
||||
if selected {
|
||||
t.window.CPrint(C.ColSelected, C.Bold, ">")
|
||||
t.window.CPrint(tui.ColSelected, tui.Bold, ">")
|
||||
} else {
|
||||
t.window.Print(" ")
|
||||
}
|
||||
t.printHighlighted(result, 0, C.ColNormal, C.ColMatch, false)
|
||||
t.printHighlighted(result, 0, tui.ColNormal, tui.ColMatch, false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -695,7 +696,7 @@ func overflow(runes []rune, max int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Terminal) printHighlighted(result *Result, attr C.Attr, col1 int, col2 int, current bool) {
|
||||
func (t *Terminal) printHighlighted(result *Result, attr tui.Attr, col1 tui.ColorPair, col2 tui.ColorPair, current bool) {
|
||||
item := result.item
|
||||
|
||||
// Overflow
|
||||
@ -827,7 +828,7 @@ func (t *Terminal) printPreview() {
|
||||
if t.previewer.offset > 0 {
|
||||
offset := fmt.Sprintf("%d/%d", t.previewer.offset+1, t.previewer.lines)
|
||||
t.pwindow.Move(0, t.pwindow.Width-len(offset))
|
||||
t.pwindow.CPrint(C.ColInfo, C.Reverse, offset)
|
||||
t.pwindow.CPrint(tui.ColInfo, tui.Reverse, offset)
|
||||
}
|
||||
}
|
||||
|
||||
@ -858,11 +859,10 @@ func (t *Terminal) printAll() {
|
||||
func (t *Terminal) refresh() {
|
||||
if !t.suppress {
|
||||
if t.isPreviewEnabled() {
|
||||
t.bwindow.Refresh()
|
||||
t.pwindow.Refresh()
|
||||
tui.RefreshWindows([]*tui.Window{t.bwindow, t.pwindow, t.window})
|
||||
} else {
|
||||
tui.RefreshWindows([]*tui.Window{t.window})
|
||||
}
|
||||
t.window.Refresh()
|
||||
C.DoUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
@ -912,10 +912,10 @@ func (t *Terminal) rubout(pattern string) {
|
||||
t.input = append(t.input[:t.cx], after...)
|
||||
}
|
||||
|
||||
func keyMatch(key int, event C.Event) bool {
|
||||
func keyMatch(key int, event tui.Event) bool {
|
||||
return event.Type == key ||
|
||||
event.Type == C.Rune && int(event.Char) == key-C.AltZ ||
|
||||
event.Type == C.Mouse && key == C.DoubleClick && event.MouseEvent.Double
|
||||
event.Type == tui.Rune && int(event.Char) == key-tui.AltZ ||
|
||||
event.Type == tui.Mouse && key == tui.DoubleClick && event.MouseEvent.Double
|
||||
}
|
||||
|
||||
func quoteEntry(entry string) string {
|
||||
@ -980,7 +980,7 @@ func (t *Terminal) executeCommand(template string, items []*Item) {
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
C.Endwin()
|
||||
tui.Pause()
|
||||
cmd.Run()
|
||||
t.refresh()
|
||||
}
|
||||
@ -1014,7 +1014,7 @@ func (t *Terminal) Loop() {
|
||||
}()
|
||||
|
||||
resizeChan := make(chan os.Signal, 1)
|
||||
signal.Notify(resizeChan, syscall.SIGWINCH)
|
||||
notifyOnResize(resizeChan) // Non-portable
|
||||
go func() {
|
||||
for {
|
||||
<-resizeChan
|
||||
@ -1126,12 +1126,11 @@ func (t *Terminal) Loop() {
|
||||
case reqRefresh:
|
||||
t.suppress = false
|
||||
case reqRedraw:
|
||||
C.Clear()
|
||||
C.Endwin()
|
||||
C.Refresh()
|
||||
tui.Clear()
|
||||
tui.Refresh()
|
||||
t.printAll()
|
||||
case reqClose:
|
||||
C.Close()
|
||||
tui.Close()
|
||||
if t.output() {
|
||||
exit(exitOk)
|
||||
}
|
||||
@ -1144,11 +1143,11 @@ func (t *Terminal) Loop() {
|
||||
case reqPreviewRefresh:
|
||||
t.printPreview()
|
||||
case reqPrintQuery:
|
||||
C.Close()
|
||||
tui.Close()
|
||||
t.printer(string(t.input))
|
||||
exit(exitOk)
|
||||
case reqQuit:
|
||||
C.Close()
|
||||
tui.Close()
|
||||
exit(exitInterrupt)
|
||||
}
|
||||
}
|
||||
@ -1161,7 +1160,7 @@ func (t *Terminal) Loop() {
|
||||
|
||||
looping := true
|
||||
for looping {
|
||||
event := C.GetChar()
|
||||
event := tui.GetChar()
|
||||
|
||||
t.mutex.Lock()
|
||||
previousInput := t.input
|
||||
@ -1445,7 +1444,7 @@ func (t *Terminal) Loop() {
|
||||
// Double-click
|
||||
if my >= min {
|
||||
if t.vset(t.offset+my-min) && t.cy < t.merger.Length() {
|
||||
return doAction(t.keymap[C.DoubleClick], C.DoubleClick)
|
||||
return doAction(t.keymap[tui.DoubleClick], tui.DoubleClick)
|
||||
}
|
||||
}
|
||||
} else if me.Down {
|
||||
@ -1468,8 +1467,8 @@ func (t *Terminal) Loop() {
|
||||
mapkey := event.Type
|
||||
if t.jumping == jumpDisabled {
|
||||
action := t.keymap[mapkey]
|
||||
if mapkey == C.Rune {
|
||||
mapkey = int(event.Char) + int(C.AltZ)
|
||||
if mapkey == tui.Rune {
|
||||
mapkey = int(event.Char) + int(tui.AltZ)
|
||||
if act, prs := t.keymap[mapkey]; prs {
|
||||
action = act
|
||||
}
|
||||
@ -1484,7 +1483,7 @@ func (t *Terminal) Loop() {
|
||||
}
|
||||
changed = string(previousInput) != string(t.input)
|
||||
} else {
|
||||
if mapkey == C.Rune {
|
||||
if mapkey == tui.Rune {
|
||||
if idx := strings.IndexRune(t.jumpLabels, event.Char); idx >= 0 && idx < t.maxItems() && idx < t.merger.Length() {
|
||||
t.cy = idx + t.offset
|
||||
if t.jumping == jumpAcceptEnabled {
|
||||
|
13
src/terminal_unix.go
Normal file
13
src/terminal_unix.go
Normal file
@ -0,0 +1,13 @@
|
||||
// +build !windows
|
||||
|
||||
package fzf
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func notifyOnResize(resizeChan chan<- os.Signal) {
|
||||
signal.Notify(resizeChan, syscall.SIGWINCH)
|
||||
}
|
11
src/terminal_windows.go
Normal file
11
src/terminal_windows.go
Normal file
@ -0,0 +1,11 @@
|
||||
// +build windows
|
||||
|
||||
package fzf
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func notifyOnResize(resizeChan chan<- os.Signal) {
|
||||
// TODO
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
package curses
|
||||
// +build !windows
|
||||
// +build !termbox
|
||||
|
||||
package tui
|
||||
|
||||
/*
|
||||
#include <ncurses.h>
|
||||
@ -10,7 +13,6 @@ package curses
|
||||
SCREEN *c_newterm () {
|
||||
return newterm(NULL, stderr, stdin);
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@ -23,6 +25,10 @@ import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type ColorPair int16
|
||||
type Attr C.int
|
||||
type WindowImpl C.WINDOW
|
||||
|
||||
const (
|
||||
Bold = C.A_BOLD
|
||||
Dim = C.A_DIM
|
||||
@ -31,89 +37,13 @@ const (
|
||||
Underline = C.A_UNDERLINE
|
||||
)
|
||||
|
||||
type Attr C.int
|
||||
|
||||
// Types of user action
|
||||
const (
|
||||
Rune = iota
|
||||
|
||||
CtrlA
|
||||
CtrlB
|
||||
CtrlC
|
||||
CtrlD
|
||||
CtrlE
|
||||
CtrlF
|
||||
CtrlG
|
||||
CtrlH
|
||||
Tab
|
||||
CtrlJ
|
||||
CtrlK
|
||||
CtrlL
|
||||
CtrlM
|
||||
CtrlN
|
||||
CtrlO
|
||||
CtrlP
|
||||
CtrlQ
|
||||
CtrlR
|
||||
CtrlS
|
||||
CtrlT
|
||||
CtrlU
|
||||
CtrlV
|
||||
CtrlW
|
||||
CtrlX
|
||||
CtrlY
|
||||
CtrlZ
|
||||
ESC
|
||||
|
||||
Invalid
|
||||
Mouse
|
||||
DoubleClick
|
||||
|
||||
BTab
|
||||
BSpace
|
||||
|
||||
Del
|
||||
PgUp
|
||||
PgDn
|
||||
|
||||
Up
|
||||
Down
|
||||
Left
|
||||
Right
|
||||
Home
|
||||
End
|
||||
|
||||
SLeft
|
||||
SRight
|
||||
|
||||
F1
|
||||
F2
|
||||
F3
|
||||
F4
|
||||
F5
|
||||
F6
|
||||
F7
|
||||
F8
|
||||
F9
|
||||
F10
|
||||
|
||||
AltEnter
|
||||
AltSpace
|
||||
AltSlash
|
||||
AltBS
|
||||
AltA
|
||||
AltB
|
||||
AltC
|
||||
AltD
|
||||
AltE
|
||||
AltF
|
||||
|
||||
AltZ = AltA + 'z' - 'a'
|
||||
AttrRegular Attr = 0
|
||||
)
|
||||
|
||||
// Pallete
|
||||
const (
|
||||
_ = iota
|
||||
ColDefault ColorPair = iota
|
||||
ColNormal
|
||||
ColPrompt
|
||||
ColMatch
|
||||
@ -128,193 +58,26 @@ const (
|
||||
ColUser // Should be the last entry
|
||||
)
|
||||
|
||||
const (
|
||||
doubleClickDuration = 500 * time.Millisecond
|
||||
colDefault = -1
|
||||
colUndefined = -2
|
||||
)
|
||||
|
||||
type ColorTheme struct {
|
||||
Fg int16
|
||||
Bg int16
|
||||
DarkBg int16
|
||||
Prompt int16
|
||||
Match int16
|
||||
Current int16
|
||||
CurrentMatch int16
|
||||
Spinner int16
|
||||
Info int16
|
||||
Cursor int16
|
||||
Selected int16
|
||||
Header int16
|
||||
Border int16
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Type int
|
||||
Char rune
|
||||
MouseEvent *MouseEvent
|
||||
}
|
||||
|
||||
type MouseEvent struct {
|
||||
Y int
|
||||
X int
|
||||
S int
|
||||
Down bool
|
||||
Double bool
|
||||
Mod bool
|
||||
}
|
||||
|
||||
var (
|
||||
_buf []byte
|
||||
_in *os.File
|
||||
_color bool
|
||||
_colorFn func(int, Attr) C.int
|
||||
_colorMap map[int]int
|
||||
_prevDownTime time.Time
|
||||
_clickY []int
|
||||
_screen *C.SCREEN
|
||||
Default16 *ColorTheme
|
||||
Dark256 *ColorTheme
|
||||
Light256 *ColorTheme
|
||||
_in *os.File
|
||||
_screen *C.SCREEN
|
||||
_colorMap map[int]ColorPair
|
||||
_colorFn func(ColorPair, Attr) C.int
|
||||
)
|
||||
|
||||
type Window struct {
|
||||
win *C.WINDOW
|
||||
Top int
|
||||
Left int
|
||||
Width int
|
||||
Height int
|
||||
}
|
||||
|
||||
func NewWindow(top int, left int, width int, height int, border bool) *Window {
|
||||
win := C.newwin(C.int(height), C.int(width), C.int(top), C.int(left))
|
||||
if _color {
|
||||
C.wbkgd(win, C.chtype(C.COLOR_PAIR(ColNormal)))
|
||||
}
|
||||
if border {
|
||||
attr := _colorFn(ColBorder, 0)
|
||||
C.wattron(win, attr)
|
||||
C.box(win, 0, 0)
|
||||
C.wattroff(win, attr)
|
||||
}
|
||||
|
||||
return &Window{
|
||||
win: win,
|
||||
Top: top,
|
||||
Left: left,
|
||||
Width: width,
|
||||
Height: height,
|
||||
}
|
||||
}
|
||||
|
||||
func EmptyTheme() *ColorTheme {
|
||||
return &ColorTheme{
|
||||
Fg: colUndefined,
|
||||
Bg: colUndefined,
|
||||
DarkBg: colUndefined,
|
||||
Prompt: colUndefined,
|
||||
Match: colUndefined,
|
||||
Current: colUndefined,
|
||||
CurrentMatch: colUndefined,
|
||||
Spinner: colUndefined,
|
||||
Info: colUndefined,
|
||||
Cursor: colUndefined,
|
||||
Selected: colUndefined,
|
||||
Header: colUndefined,
|
||||
Border: colUndefined}
|
||||
}
|
||||
|
||||
func init() {
|
||||
_prevDownTime = time.Unix(0, 0)
|
||||
_clickY = []int{}
|
||||
_colorMap = make(map[int]int)
|
||||
Default16 = &ColorTheme{
|
||||
Fg: colDefault,
|
||||
Bg: colDefault,
|
||||
DarkBg: C.COLOR_BLACK,
|
||||
Prompt: C.COLOR_BLUE,
|
||||
Match: C.COLOR_GREEN,
|
||||
Current: C.COLOR_YELLOW,
|
||||
CurrentMatch: C.COLOR_GREEN,
|
||||
Spinner: C.COLOR_GREEN,
|
||||
Info: C.COLOR_WHITE,
|
||||
Cursor: C.COLOR_RED,
|
||||
Selected: C.COLOR_MAGENTA,
|
||||
Header: C.COLOR_CYAN,
|
||||
Border: C.COLOR_BLACK}
|
||||
Dark256 = &ColorTheme{
|
||||
Fg: colDefault,
|
||||
Bg: colDefault,
|
||||
DarkBg: 236,
|
||||
Prompt: 110,
|
||||
Match: 108,
|
||||
Current: 254,
|
||||
CurrentMatch: 151,
|
||||
Spinner: 148,
|
||||
Info: 144,
|
||||
Cursor: 161,
|
||||
Selected: 168,
|
||||
Header: 109,
|
||||
Border: 59}
|
||||
Light256 = &ColorTheme{
|
||||
Fg: colDefault,
|
||||
Bg: colDefault,
|
||||
DarkBg: 251,
|
||||
Prompt: 25,
|
||||
Match: 66,
|
||||
Current: 237,
|
||||
CurrentMatch: 23,
|
||||
Spinner: 65,
|
||||
Info: 101,
|
||||
Cursor: 161,
|
||||
Selected: 168,
|
||||
Header: 31,
|
||||
Border: 145}
|
||||
_colorMap = make(map[int]ColorPair)
|
||||
}
|
||||
|
||||
func attrColored(pair int, a Attr) C.int {
|
||||
var attr C.int
|
||||
if pair > 0 {
|
||||
attr = C.COLOR_PAIR(C.int(pair))
|
||||
func (a Attr) Merge(b Attr) Attr {
|
||||
return a | b
|
||||
}
|
||||
|
||||
func DefaultTheme() *ColorTheme {
|
||||
if C.tigetnum(C.CString("colors")) >= 256 {
|
||||
return Dark256
|
||||
}
|
||||
return attr | C.int(a)
|
||||
}
|
||||
|
||||
func attrMono(pair int, a Attr) C.int {
|
||||
var attr C.int
|
||||
switch pair {
|
||||
case ColCurrent:
|
||||
if a&C.A_BOLD == C.A_BOLD {
|
||||
attr = C.A_REVERSE
|
||||
}
|
||||
case ColMatch:
|
||||
attr = C.A_UNDERLINE
|
||||
case ColCurrentMatch:
|
||||
attr = C.A_UNDERLINE | C.A_REVERSE
|
||||
}
|
||||
if a&C.A_BOLD == C.A_BOLD {
|
||||
attr = attr | C.A_BOLD
|
||||
}
|
||||
return attr
|
||||
}
|
||||
|
||||
func MaxX() int {
|
||||
return int(C.COLS)
|
||||
}
|
||||
|
||||
func MaxY() int {
|
||||
return int(C.LINES)
|
||||
}
|
||||
|
||||
func getch(nonblock bool) int {
|
||||
b := make([]byte, 1)
|
||||
syscall.SetNonblock(int(_in.Fd()), nonblock)
|
||||
_, err := _in.Read(b)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return int(b[0])
|
||||
return Default16
|
||||
}
|
||||
|
||||
func Init(theme *ColorTheme, black bool, mouse bool) {
|
||||
@ -344,52 +107,19 @@ func Init(theme *ColorTheme, black bool, mouse bool) {
|
||||
_color = theme != nil
|
||||
if _color {
|
||||
C.start_color()
|
||||
var baseTheme *ColorTheme
|
||||
if C.tigetnum(C.CString("colors")) >= 256 {
|
||||
baseTheme = Dark256
|
||||
} else {
|
||||
baseTheme = Default16
|
||||
}
|
||||
initPairs(baseTheme, theme, black)
|
||||
C.bkgd(C.chtype(C.COLOR_PAIR(ColNormal)))
|
||||
InitTheme(theme, black)
|
||||
initPairs(theme)
|
||||
C.bkgd(C.chtype(C.COLOR_PAIR(C.int(ColNormal))))
|
||||
_colorFn = attrColored
|
||||
} else {
|
||||
_colorFn = attrMono
|
||||
}
|
||||
}
|
||||
|
||||
func override(baseTheme *ColorTheme, theme *ColorTheme) {
|
||||
o := func(a int16, b int16) int16 {
|
||||
if b == colUndefined {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
theme.Fg = o(baseTheme.Fg, theme.Fg)
|
||||
theme.Bg = o(baseTheme.Bg, theme.Bg)
|
||||
theme.DarkBg = o(baseTheme.DarkBg, theme.DarkBg)
|
||||
theme.Prompt = o(baseTheme.Prompt, theme.Prompt)
|
||||
theme.Match = o(baseTheme.Match, theme.Match)
|
||||
theme.Current = o(baseTheme.Current, theme.Current)
|
||||
theme.CurrentMatch = o(baseTheme.CurrentMatch, theme.CurrentMatch)
|
||||
theme.Spinner = o(baseTheme.Spinner, theme.Spinner)
|
||||
theme.Info = o(baseTheme.Info, theme.Info)
|
||||
theme.Cursor = o(baseTheme.Cursor, theme.Cursor)
|
||||
theme.Selected = o(baseTheme.Selected, theme.Selected)
|
||||
theme.Header = o(baseTheme.Header, theme.Header)
|
||||
theme.Border = o(baseTheme.Border, theme.Border)
|
||||
}
|
||||
|
||||
func initPairs(baseTheme *ColorTheme, theme *ColorTheme, black bool) {
|
||||
if black {
|
||||
theme.Bg = C.COLOR_BLACK
|
||||
}
|
||||
// Updates theme
|
||||
override(baseTheme, theme)
|
||||
|
||||
func initPairs(theme *ColorTheme) {
|
||||
C.assume_default_colors(C.int(theme.Fg), C.int(theme.Bg))
|
||||
initPair := func(group C.short, fg int16, bg int16) {
|
||||
C.init_pair(group, C.short(fg), C.short(bg))
|
||||
initPair := func(group ColorPair, fg Color, bg Color) {
|
||||
C.init_pair(C.short(group), C.short(fg), C.short(bg))
|
||||
}
|
||||
initPair(ColNormal, theme.Fg, theme.Bg)
|
||||
initPair(ColPrompt, theme.Prompt, theme.Bg)
|
||||
@ -404,11 +134,161 @@ func initPairs(baseTheme *ColorTheme, theme *ColorTheme, black bool) {
|
||||
initPair(ColBorder, theme.Border, theme.Bg)
|
||||
}
|
||||
|
||||
func Pause() {
|
||||
C.endwin()
|
||||
}
|
||||
|
||||
func Close() {
|
||||
C.endwin()
|
||||
C.delscreen(_screen)
|
||||
}
|
||||
|
||||
func NewWindow(top int, left int, width int, height int, border bool) *Window {
|
||||
win := C.newwin(C.int(height), C.int(width), C.int(top), C.int(left))
|
||||
if _color {
|
||||
C.wbkgd(win, C.chtype(C.COLOR_PAIR(C.int(ColNormal))))
|
||||
}
|
||||
if border {
|
||||
attr := _colorFn(ColBorder, 0)
|
||||
C.wattron(win, attr)
|
||||
C.box(win, 0, 0)
|
||||
C.wattroff(win, attr)
|
||||
}
|
||||
|
||||
return &Window{
|
||||
impl: (*WindowImpl)(win),
|
||||
Top: top,
|
||||
Left: left,
|
||||
Width: width,
|
||||
Height: height,
|
||||
}
|
||||
}
|
||||
|
||||
func attrColored(pair ColorPair, a Attr) C.int {
|
||||
var attr C.int
|
||||
if pair > 0 {
|
||||
attr = C.COLOR_PAIR(C.int(pair))
|
||||
}
|
||||
return attr | C.int(a)
|
||||
}
|
||||
|
||||
func attrMono(pair ColorPair, a Attr) C.int {
|
||||
var attr C.int
|
||||
switch pair {
|
||||
case ColCurrent:
|
||||
if C.int(a)&C.A_BOLD == C.A_BOLD {
|
||||
attr = C.A_REVERSE
|
||||
}
|
||||
case ColMatch:
|
||||
attr = C.A_UNDERLINE
|
||||
case ColCurrentMatch:
|
||||
attr = C.A_UNDERLINE | C.A_REVERSE
|
||||
}
|
||||
if C.int(a)&C.A_BOLD == C.A_BOLD {
|
||||
attr = attr | C.A_BOLD
|
||||
}
|
||||
return attr
|
||||
}
|
||||
|
||||
func MaxX() int {
|
||||
return int(C.COLS)
|
||||
}
|
||||
|
||||
func MaxY() int {
|
||||
return int(C.LINES)
|
||||
}
|
||||
|
||||
func (w *Window) win() *C.WINDOW {
|
||||
return (*C.WINDOW)(w.impl)
|
||||
}
|
||||
|
||||
func (w *Window) Close() {
|
||||
C.delwin(w.win())
|
||||
}
|
||||
|
||||
func (w *Window) Enclose(y int, x int) bool {
|
||||
return bool(C.wenclose(w.win(), C.int(y), C.int(x)))
|
||||
}
|
||||
|
||||
func (w *Window) Move(y int, x int) {
|
||||
C.wmove(w.win(), C.int(y), C.int(x))
|
||||
}
|
||||
|
||||
func (w *Window) MoveAndClear(y int, x int) {
|
||||
w.Move(y, x)
|
||||
C.wclrtoeol(w.win())
|
||||
}
|
||||
|
||||
func (w *Window) Print(text string) {
|
||||
C.waddstr(w.win(), C.CString(strings.Map(func(r rune) rune {
|
||||
if r < 32 {
|
||||
return -1
|
||||
}
|
||||
return r
|
||||
}, text)))
|
||||
}
|
||||
|
||||
func (w *Window) CPrint(pair ColorPair, a Attr, text string) {
|
||||
attr := _colorFn(pair, a)
|
||||
C.wattron(w.win(), attr)
|
||||
w.Print(text)
|
||||
C.wattroff(w.win(), attr)
|
||||
}
|
||||
|
||||
func Clear() {
|
||||
C.clear()
|
||||
C.endwin()
|
||||
}
|
||||
|
||||
func Refresh() {
|
||||
C.refresh()
|
||||
}
|
||||
|
||||
func (w *Window) Erase() {
|
||||
C.werase(w.win())
|
||||
}
|
||||
|
||||
func (w *Window) Fill(str string) bool {
|
||||
return C.waddstr(w.win(), C.CString(str)) == C.OK
|
||||
}
|
||||
|
||||
func (w *Window) CFill(str string, fg Color, bg Color, a Attr) bool {
|
||||
attr := _colorFn(PairFor(fg, bg), a)
|
||||
C.wattron(w.win(), attr)
|
||||
ret := w.Fill(str)
|
||||
C.wattroff(w.win(), attr)
|
||||
return ret
|
||||
}
|
||||
|
||||
func RefreshWindows(windows []*Window) {
|
||||
for _, w := range windows {
|
||||
C.wnoutrefresh(w.win())
|
||||
}
|
||||
C.doupdate()
|
||||
}
|
||||
|
||||
func PairFor(fg Color, bg Color) ColorPair {
|
||||
key := (int(fg) << 8) + int(bg)
|
||||
if found, prs := _colorMap[key]; prs {
|
||||
return found
|
||||
}
|
||||
|
||||
id := ColorPair(len(_colorMap) + int(ColUser))
|
||||
C.init_pair(C.short(id), C.short(fg), C.short(bg))
|
||||
_colorMap[key] = id
|
||||
return id
|
||||
}
|
||||
|
||||
func getch(nonblock bool) int {
|
||||
b := make([]byte, 1)
|
||||
syscall.SetNonblock(int(_in.Fd()), nonblock)
|
||||
_, err := _in.Read(b)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return int(b[0])
|
||||
}
|
||||
|
||||
func GetBytes() []byte {
|
||||
c := getch(false)
|
||||
_buf = append(_buf, byte(c))
|
||||
@ -631,84 +511,3 @@ func GetChar() Event {
|
||||
sz = rsz
|
||||
return Event{Rune, r, nil}
|
||||
}
|
||||
|
||||
func (w *Window) Close() {
|
||||
C.delwin(w.win)
|
||||
}
|
||||
|
||||
func (w *Window) Enclose(y int, x int) bool {
|
||||
return bool(C.wenclose(w.win, C.int(y), C.int(x)))
|
||||
}
|
||||
|
||||
func (w *Window) Move(y int, x int) {
|
||||
C.wmove(w.win, C.int(y), C.int(x))
|
||||
}
|
||||
|
||||
func (w *Window) MoveAndClear(y int, x int) {
|
||||
w.Move(y, x)
|
||||
C.wclrtoeol(w.win)
|
||||
}
|
||||
|
||||
func (w *Window) Print(text string) {
|
||||
C.waddstr(w.win, C.CString(strings.Map(func(r rune) rune {
|
||||
if r < 32 {
|
||||
return -1
|
||||
}
|
||||
return r
|
||||
}, text)))
|
||||
}
|
||||
|
||||
func (w *Window) CPrint(pair int, a Attr, text string) {
|
||||
attr := _colorFn(pair, a)
|
||||
C.wattron(w.win, attr)
|
||||
w.Print(text)
|
||||
C.wattroff(w.win, attr)
|
||||
}
|
||||
|
||||
func Clear() {
|
||||
C.clear()
|
||||
}
|
||||
|
||||
func Endwin() {
|
||||
C.endwin()
|
||||
}
|
||||
|
||||
func Refresh() {
|
||||
C.refresh()
|
||||
}
|
||||
|
||||
func (w *Window) Erase() {
|
||||
C.werase(w.win)
|
||||
}
|
||||
|
||||
func (w *Window) Fill(str string) bool {
|
||||
return C.waddstr(w.win, C.CString(str)) == C.OK
|
||||
}
|
||||
|
||||
func (w *Window) CFill(str string, fg int, bg int, a Attr) bool {
|
||||
attr := _colorFn(PairFor(fg, bg), a)
|
||||
C.wattron(w.win, attr)
|
||||
ret := w.Fill(str)
|
||||
C.wattroff(w.win, attr)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (w *Window) Refresh() {
|
||||
C.wnoutrefresh(w.win)
|
||||
}
|
||||
|
||||
func DoUpdate() {
|
||||
C.doupdate()
|
||||
}
|
||||
|
||||
func PairFor(fg int, bg int) int {
|
||||
key := (fg << 8) + bg
|
||||
if found, prs := _colorMap[key]; prs {
|
||||
return found
|
||||
}
|
||||
|
||||
id := len(_colorMap) + ColUser
|
||||
C.init_pair(C.short(id), C.short(fg), C.short(bg))
|
||||
_colorMap[key] = id
|
||||
return id
|
||||
}
|
151
src/tui/termbox.go
Normal file
151
src/tui/termbox.go
Normal file
@ -0,0 +1,151 @@
|
||||
// +build termbox windows
|
||||
|
||||
package tui
|
||||
|
||||
import (
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
type ColorPair [2]Color
|
||||
type Attr uint16
|
||||
type WindowImpl int // FIXME
|
||||
|
||||
const (
|
||||
// TODO
|
||||
_ = iota
|
||||
Bold
|
||||
Dim
|
||||
Blink
|
||||
Reverse
|
||||
Underline
|
||||
)
|
||||
|
||||
const (
|
||||
AttrRegular Attr = 0
|
||||
)
|
||||
|
||||
var (
|
||||
ColDefault = ColorPair{colDefault, colDefault}
|
||||
ColNormal ColorPair
|
||||
ColPrompt ColorPair
|
||||
ColMatch ColorPair
|
||||
ColCurrent ColorPair
|
||||
ColCurrentMatch ColorPair
|
||||
ColSpinner ColorPair
|
||||
ColInfo ColorPair
|
||||
ColCursor ColorPair
|
||||
ColSelected ColorPair
|
||||
ColHeader ColorPair
|
||||
ColBorder ColorPair
|
||||
ColUser ColorPair
|
||||
)
|
||||
|
||||
func DefaultTheme() *ColorTheme {
|
||||
if termbox.SetOutputMode(termbox.OutputCurrent) == termbox.Output256 {
|
||||
return Dark256
|
||||
}
|
||||
return Default16
|
||||
}
|
||||
|
||||
func PairFor(fg Color, bg Color) ColorPair {
|
||||
return [2]Color{fg, bg}
|
||||
}
|
||||
|
||||
func (a Attr) Merge(b Attr) Attr {
|
||||
return a | b
|
||||
}
|
||||
|
||||
func Init(theme *ColorTheme, black bool, mouse bool) {
|
||||
ColNormal = ColorPair{theme.Fg, theme.Bg}
|
||||
ColPrompt = ColorPair{theme.Prompt, theme.Bg}
|
||||
ColMatch = ColorPair{theme.Match, theme.Bg}
|
||||
ColCurrent = ColorPair{theme.Current, theme.DarkBg}
|
||||
ColCurrentMatch = ColorPair{theme.CurrentMatch, theme.DarkBg}
|
||||
ColSpinner = ColorPair{theme.Spinner, theme.Bg}
|
||||
ColInfo = ColorPair{theme.Info, theme.Bg}
|
||||
ColCursor = ColorPair{theme.Cursor, theme.DarkBg}
|
||||
ColSelected = ColorPair{theme.Selected, theme.DarkBg}
|
||||
ColHeader = ColorPair{theme.Header, theme.Bg}
|
||||
ColBorder = ColorPair{theme.Border, theme.Bg}
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
func MaxX() int {
|
||||
// TODO
|
||||
return 80
|
||||
}
|
||||
|
||||
func MaxY() int {
|
||||
// TODO
|
||||
return 24
|
||||
}
|
||||
|
||||
func Clear() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func Refresh() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func GetChar() Event {
|
||||
// TODO
|
||||
return Event{}
|
||||
}
|
||||
|
||||
func Pause() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func Close() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func RefreshWindows(windows []*Window) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func NewWindow(top int, left int, width int, height int, border bool) *Window {
|
||||
// TODO
|
||||
return &Window{}
|
||||
}
|
||||
|
||||
func (w *Window) Close() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func (w *Window) Erase() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func (w *Window) Enclose(y int, x int) bool {
|
||||
// TODO
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *Window) Move(y int, x int) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func (w *Window) MoveAndClear(y int, x int) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func (w *Window) Print(text string) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func (w *Window) CPrint(pair ColorPair, a Attr, text string) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func (w *Window) Fill(str string) bool {
|
||||
// TODO
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *Window) CFill(str string, fg Color, bg Color, a Attr) bool {
|
||||
// TODO
|
||||
return false
|
||||
}
|
250
src/tui/tui.go
Normal file
250
src/tui/tui.go
Normal file
@ -0,0 +1,250 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Types of user action
|
||||
const (
|
||||
Rune = iota
|
||||
|
||||
CtrlA
|
||||
CtrlB
|
||||
CtrlC
|
||||
CtrlD
|
||||
CtrlE
|
||||
CtrlF
|
||||
CtrlG
|
||||
CtrlH
|
||||
Tab
|
||||
CtrlJ
|
||||
CtrlK
|
||||
CtrlL
|
||||
CtrlM
|
||||
CtrlN
|
||||
CtrlO
|
||||
CtrlP
|
||||
CtrlQ
|
||||
CtrlR
|
||||
CtrlS
|
||||
CtrlT
|
||||
CtrlU
|
||||
CtrlV
|
||||
CtrlW
|
||||
CtrlX
|
||||
CtrlY
|
||||
CtrlZ
|
||||
ESC
|
||||
|
||||
Invalid
|
||||
Mouse
|
||||
DoubleClick
|
||||
|
||||
BTab
|
||||
BSpace
|
||||
|
||||
Del
|
||||
PgUp
|
||||
PgDn
|
||||
|
||||
Up
|
||||
Down
|
||||
Left
|
||||
Right
|
||||
Home
|
||||
End
|
||||
|
||||
SLeft
|
||||
SRight
|
||||
|
||||
F1
|
||||
F2
|
||||
F3
|
||||
F4
|
||||
F5
|
||||
F6
|
||||
F7
|
||||
F8
|
||||
F9
|
||||
F10
|
||||
|
||||
AltEnter
|
||||
AltSpace
|
||||
AltSlash
|
||||
AltBS
|
||||
AltA
|
||||
AltB
|
||||
AltC
|
||||
AltD
|
||||
AltE
|
||||
AltF
|
||||
|
||||
AltZ = AltA + 'z' - 'a'
|
||||
)
|
||||
|
||||
const (
|
||||
doubleClickDuration = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
type Color int16
|
||||
|
||||
const (
|
||||
colUndefined Color = -2
|
||||
colDefault = -1
|
||||
)
|
||||
|
||||
const (
|
||||
colBlack Color = iota
|
||||
colRed
|
||||
colGreen
|
||||
colYellow
|
||||
colBlue
|
||||
colMagenta
|
||||
colCyan
|
||||
colWhite
|
||||
)
|
||||
|
||||
type ColorTheme struct {
|
||||
Fg Color
|
||||
Bg Color
|
||||
DarkBg Color
|
||||
Prompt Color
|
||||
Match Color
|
||||
Current Color
|
||||
CurrentMatch Color
|
||||
Spinner Color
|
||||
Info Color
|
||||
Cursor Color
|
||||
Selected Color
|
||||
Header Color
|
||||
Border Color
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Type int
|
||||
Char rune
|
||||
MouseEvent *MouseEvent
|
||||
}
|
||||
|
||||
type MouseEvent struct {
|
||||
Y int
|
||||
X int
|
||||
S int
|
||||
Down bool
|
||||
Double bool
|
||||
Mod bool
|
||||
}
|
||||
|
||||
var (
|
||||
_buf []byte
|
||||
_color bool
|
||||
_prevDownTime time.Time
|
||||
_clickY []int
|
||||
Default16 *ColorTheme
|
||||
Dark256 *ColorTheme
|
||||
Light256 *ColorTheme
|
||||
)
|
||||
|
||||
type Window struct {
|
||||
impl *WindowImpl
|
||||
Top int
|
||||
Left int
|
||||
Width int
|
||||
Height int
|
||||
}
|
||||
|
||||
func EmptyTheme() *ColorTheme {
|
||||
return &ColorTheme{
|
||||
Fg: colUndefined,
|
||||
Bg: colUndefined,
|
||||
DarkBg: colUndefined,
|
||||
Prompt: colUndefined,
|
||||
Match: colUndefined,
|
||||
Current: colUndefined,
|
||||
CurrentMatch: colUndefined,
|
||||
Spinner: colUndefined,
|
||||
Info: colUndefined,
|
||||
Cursor: colUndefined,
|
||||
Selected: colUndefined,
|
||||
Header: colUndefined,
|
||||
Border: colUndefined}
|
||||
}
|
||||
|
||||
func init() {
|
||||
_prevDownTime = time.Unix(0, 0)
|
||||
_clickY = []int{}
|
||||
Default16 = &ColorTheme{
|
||||
Fg: colDefault,
|
||||
Bg: colDefault,
|
||||
DarkBg: colBlack,
|
||||
Prompt: colBlue,
|
||||
Match: colGreen,
|
||||
Current: colYellow,
|
||||
CurrentMatch: colGreen,
|
||||
Spinner: colGreen,
|
||||
Info: colWhite,
|
||||
Cursor: colRed,
|
||||
Selected: colMagenta,
|
||||
Header: colCyan,
|
||||
Border: colBlack}
|
||||
Dark256 = &ColorTheme{
|
||||
Fg: colDefault,
|
||||
Bg: colDefault,
|
||||
DarkBg: 236,
|
||||
Prompt: 110,
|
||||
Match: 108,
|
||||
Current: 254,
|
||||
CurrentMatch: 151,
|
||||
Spinner: 148,
|
||||
Info: 144,
|
||||
Cursor: 161,
|
||||
Selected: 168,
|
||||
Header: 109,
|
||||
Border: 59}
|
||||
Light256 = &ColorTheme{
|
||||
Fg: colDefault,
|
||||
Bg: colDefault,
|
||||
DarkBg: 251,
|
||||
Prompt: 25,
|
||||
Match: 66,
|
||||
Current: 237,
|
||||
CurrentMatch: 23,
|
||||
Spinner: 65,
|
||||
Info: 101,
|
||||
Cursor: 161,
|
||||
Selected: 168,
|
||||
Header: 31,
|
||||
Border: 145}
|
||||
}
|
||||
|
||||
func InitTheme(theme *ColorTheme, black bool) {
|
||||
_color = theme != nil
|
||||
if !_color {
|
||||
return
|
||||
}
|
||||
|
||||
baseTheme := DefaultTheme()
|
||||
if black {
|
||||
theme.Bg = colBlack
|
||||
}
|
||||
|
||||
o := func(a Color, b Color) Color {
|
||||
if b == colUndefined {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
theme.Fg = o(baseTheme.Fg, theme.Fg)
|
||||
theme.Bg = o(baseTheme.Bg, theme.Bg)
|
||||
theme.DarkBg = o(baseTheme.DarkBg, theme.DarkBg)
|
||||
theme.Prompt = o(baseTheme.Prompt, theme.Prompt)
|
||||
theme.Match = o(baseTheme.Match, theme.Match)
|
||||
theme.Current = o(baseTheme.Current, theme.Current)
|
||||
theme.CurrentMatch = o(baseTheme.CurrentMatch, theme.CurrentMatch)
|
||||
theme.Spinner = o(baseTheme.Spinner, theme.Spinner)
|
||||
theme.Info = o(baseTheme.Info, theme.Info)
|
||||
theme.Cursor = o(baseTheme.Cursor, theme.Cursor)
|
||||
theme.Selected = o(baseTheme.Selected, theme.Selected)
|
||||
theme.Header = o(baseTheme.Header, theme.Header)
|
||||
theme.Border = o(baseTheme.Border, theme.Border)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package curses
|
||||
package tui
|
||||
|
||||
import (
|
||||
"testing"
|
@ -1,13 +1,11 @@
|
||||
package util
|
||||
|
||||
// #include <unistd.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/junegunn/go-isatty"
|
||||
)
|
||||
|
||||
// Max returns the largest integer
|
||||
@ -95,14 +93,5 @@ func DurWithin(
|
||||
|
||||
// IsTty returns true is stdin is a terminal
|
||||
func IsTty() bool {
|
||||
return int(C.isatty(C.int(os.Stdin.Fd()))) != 0
|
||||
}
|
||||
|
||||
// ExecCommand executes the given command with $SHELL
|
||||
func ExecCommand(command string) *exec.Cmd {
|
||||
shell := os.Getenv("SHELL")
|
||||
if len(shell) == 0 {
|
||||
shell = "sh"
|
||||
}
|
||||
return exec.Command(shell, "-c", command)
|
||||
return isatty.IsTerminal(os.Stdin.Fd())
|
||||
}
|
||||
|
17
src/util/util_unix.go
Normal file
17
src/util/util_unix.go
Normal file
@ -0,0 +1,17 @@
|
||||
// +build !windows
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// ExecCommand executes the given command with $SHELL
|
||||
func ExecCommand(command string) *exec.Cmd {
|
||||
shell := os.Getenv("SHELL")
|
||||
if len(shell) == 0 {
|
||||
shell = "sh"
|
||||
}
|
||||
return exec.Command(shell, "-c", command)
|
||||
}
|
17
src/util/util_windows.go
Normal file
17
src/util/util_windows.go
Normal file
@ -0,0 +1,17 @@
|
||||
// +build windows
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// ExecCommand executes the given command with $SHELL
|
||||
func ExecCommand(command string) *exec.Cmd {
|
||||
shell := os.Getenv("SHELL")
|
||||
if len(shell) == 0 {
|
||||
shell = "cmd"
|
||||
}
|
||||
return exec.Command(shell, "/c", command)
|
||||
}
|
Loading…
Reference in New Issue
Block a user