mirror of
https://github.com/dolthub/dolt.git
synced 2026-01-08 08:49:52 -06:00
Make termio scrolling more efficient
This commit is contained in:
committed by
Dan Willhite
parent
e2f5274714
commit
705b13cfa8
@@ -99,7 +99,7 @@ func runClient(ipfsSpec string, cInfo lib.ClientInfo) {
|
||||
t.ResetAuthors(ds)
|
||||
t.UpdateMessages(ds, nil, nil)
|
||||
|
||||
go lib.ProcessChatEvents(node, ds, events, &t, cInfo)
|
||||
go lib.ProcessChatEvents(node, ds, events, t, cInfo)
|
||||
go lib.ReceiveMessages(node, events, cInfo)
|
||||
|
||||
if err := t.Gui.MainLoop(); err != nil && err != gocui.ErrQuit {
|
||||
|
||||
@@ -6,6 +6,7 @@ package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/attic-labs/noms/go/datas"
|
||||
"github.com/attic-labs/noms/go/marshal"
|
||||
@@ -20,16 +21,20 @@ type dataPager struct {
|
||||
terms []string
|
||||
}
|
||||
|
||||
var dp dataPager
|
||||
func NewDataPager(ds datas.Dataset, mkChan chan types.String, doneChan chan struct{}, msgs types.Map, terms []string) *dataPager {
|
||||
return &dataPager{
|
||||
dataset: ds,
|
||||
msgKeyChan: mkChan,
|
||||
doneChan: doneChan,
|
||||
msgMap: msgs,
|
||||
terms: terms,
|
||||
}
|
||||
}
|
||||
|
||||
func (dp *dataPager) Close() {
|
||||
dp.doneChan <- struct{}{}
|
||||
}
|
||||
|
||||
func (dp *dataPager) IsEmpty() bool {
|
||||
return dp.msgKeyChan == nil && dp.doneChan == nil
|
||||
}
|
||||
|
||||
func (dp *dataPager) Next() (string, bool) {
|
||||
msgKey := <-dp.msgKeyChan
|
||||
if msgKey == "" {
|
||||
@@ -47,3 +52,16 @@ func (dp *dataPager) Next() (string, bool) {
|
||||
s2 := highlightTerms(s1, dp.terms)
|
||||
return s2, true
|
||||
}
|
||||
|
||||
func (dp *dataPager) Prepend(lines []string, target int) ([]string, bool) {
|
||||
new := []string{}
|
||||
m, ok := dp.Next()
|
||||
if !ok {
|
||||
return lines, false
|
||||
}
|
||||
for ; ok && len(new) < target; m, ok = dp.Next() {
|
||||
new1 := strings.Split(m, "\n")
|
||||
new = append(new1, new...)
|
||||
}
|
||||
return append(new, lines...), true
|
||||
}
|
||||
|
||||
@@ -19,10 +19,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
allViews = ""
|
||||
usersView = "users"
|
||||
messageView = "messages"
|
||||
inputView = "input"
|
||||
allViews = ""
|
||||
usersView = "users"
|
||||
messageView = "messages"
|
||||
inputView = "input"
|
||||
linestofetch = 50
|
||||
|
||||
searchPrefix = "/s"
|
||||
quitPrefix = "/q"
|
||||
@@ -31,6 +32,8 @@ const (
|
||||
type TermUI struct {
|
||||
Gui *gocui.Gui
|
||||
InSearch bool
|
||||
lines []string
|
||||
dp *dataPager
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -38,7 +41,7 @@ var (
|
||||
firstLayout = true
|
||||
)
|
||||
|
||||
func CreateTermUI(events chan ChatEvent) TermUI {
|
||||
func CreateTermUI(events chan ChatEvent) *TermUI {
|
||||
g, err := gocui.NewGui(gocui.Output256)
|
||||
d.PanicIfError(err)
|
||||
|
||||
@@ -51,12 +54,15 @@ func CreateTermUI(events chan ChatEvent) TermUI {
|
||||
}
|
||||
g.SetManagerFunc(relayout)
|
||||
|
||||
d.PanicIfError(g.SetKeybinding(allViews, gocui.KeyF1, gocui.ModNone, debugInfo))
|
||||
termUI := new(TermUI)
|
||||
termUI.Gui = g
|
||||
|
||||
d.PanicIfError(g.SetKeybinding(allViews, gocui.KeyF1, gocui.ModNone, debugInfo(termUI)))
|
||||
d.PanicIfError(g.SetKeybinding(allViews, gocui.KeyCtrlC, gocui.ModNone, quit))
|
||||
d.PanicIfError(g.SetKeybinding(allViews, gocui.KeyCtrlC, gocui.ModAlt, quitWithStack))
|
||||
d.PanicIfError(g.SetKeybinding(allViews, gocui.KeyTab, gocui.ModNone, nextView))
|
||||
d.PanicIfError(g.SetKeybinding(messageView, gocui.KeyArrowUp, gocui.ModNone, arrowUp))
|
||||
d.PanicIfError(g.SetKeybinding(messageView, gocui.KeyArrowDown, gocui.ModNone, arrowDown))
|
||||
d.PanicIfError(g.SetKeybinding(messageView, gocui.KeyArrowUp, gocui.ModNone, arrowUp(termUI)))
|
||||
d.PanicIfError(g.SetKeybinding(messageView, gocui.KeyArrowDown, gocui.ModNone, arrowDown(termUI)))
|
||||
d.PanicIfError(g.SetKeybinding(inputView, gocui.KeyEnter, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) (err error) {
|
||||
defer func() {
|
||||
v.Clear()
|
||||
@@ -79,16 +85,16 @@ func CreateTermUI(events chan ChatEvent) TermUI {
|
||||
return
|
||||
}))
|
||||
|
||||
return TermUI{Gui: g, InSearch: false}
|
||||
return termUI
|
||||
}
|
||||
|
||||
func (t TermUI) Close() {
|
||||
func (t *TermUI) Close() {
|
||||
dbg.Debug("Closing gui")
|
||||
t.Gui.Close()
|
||||
}
|
||||
|
||||
func (t TermUI) UpdateMessagesFromSync(ds datas.Dataset) {
|
||||
if t.InSearch || !textScrolledToEnd(t.Gui) {
|
||||
func (t *TermUI) UpdateMessagesFromSync(ds datas.Dataset) {
|
||||
if t.InSearch || !t.textScrolledToEnd() {
|
||||
t.Gui.Execute(func(g *gocui.Gui) (err error) {
|
||||
updateViewTitle(g, messageView, "messages (NEW!)")
|
||||
return
|
||||
@@ -98,7 +104,7 @@ func (t TermUI) UpdateMessagesFromSync(ds datas.Dataset) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t TermUI) Layout() error {
|
||||
func (t *TermUI) Layout() error {
|
||||
return layout(t.Gui)
|
||||
}
|
||||
|
||||
@@ -138,47 +144,34 @@ func layout(g *gocui.Gui) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t TermUI) UpdateMessages(ds datas.Dataset, filterIds *types.Map, terms []string) error {
|
||||
func (t *TermUI) UpdateMessages(ds datas.Dataset, filterIds *types.Map, terms []string) error {
|
||||
defer dbg.BoxF("updateMessages")()
|
||||
|
||||
t.ResetAuthors(ds)
|
||||
v, err := t.Gui.View(messageView)
|
||||
d.PanicIfError(err)
|
||||
v.Clear()
|
||||
t.lines = []string{}
|
||||
v.SetOrigin(0, 0)
|
||||
_, winHeight := v.Size()
|
||||
|
||||
if !dp.IsEmpty() {
|
||||
dp.Close()
|
||||
if t.dp != nil {
|
||||
t.dp.Close()
|
||||
}
|
||||
|
||||
doneChan := make(chan struct{})
|
||||
msgMap, msgKeyChan, err := ListMessages(ds, filterIds, doneChan)
|
||||
d.PanicIfError(err)
|
||||
dp = dataPager{
|
||||
dataset: ds,
|
||||
msgKeyChan: msgKeyChan,
|
||||
doneChan: doneChan,
|
||||
msgMap: msgMap,
|
||||
terms: terms,
|
||||
}
|
||||
t.dp = NewDataPager(ds, msgKeyChan, doneChan, msgMap, terms)
|
||||
t.lines, _ = t.dp.Prepend(t.lines, math.MaxInt(linestofetch, winHeight+10))
|
||||
|
||||
items := []string{}
|
||||
for len(items) < winHeight {
|
||||
m, ok := dp.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
items = append([]string{m}, items...)
|
||||
}
|
||||
|
||||
for _, s := range items {
|
||||
for _, s := range t.lines {
|
||||
fmt.Fprintf(v, "%s\n", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t TermUI) ResetAuthors(ds datas.Dataset) {
|
||||
func (t *TermUI) ResetAuthors(ds datas.Dataset) {
|
||||
v, err := t.Gui.View(usersView)
|
||||
d.PanicIfError(err)
|
||||
v.Clear()
|
||||
@@ -187,7 +180,7 @@ func (t TermUI) ResetAuthors(ds datas.Dataset) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t TermUI) UpdateMessagesAsync(ds datas.Dataset, sids *types.Map, terms []string) {
|
||||
func (t *TermUI) UpdateMessagesAsync(ds datas.Dataset, sids *types.Map, terms []string) {
|
||||
t.Gui.Execute(func(_ *gocui.Gui) error {
|
||||
err := t.UpdateMessages(ds, sids, terms)
|
||||
d.PanicIfError(err)
|
||||
@@ -195,51 +188,35 @@ func (t TermUI) UpdateMessagesAsync(ds datas.Dataset, sids *types.Map, terms []s
|
||||
})
|
||||
}
|
||||
|
||||
func prependMessages(v *gocui.View) int {
|
||||
buf := viewBuffer(v)
|
||||
if len(buf) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
msg, ok := dp.Next()
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
|
||||
v.Clear()
|
||||
fmt.Fprintf(v, "%s\n", msg)
|
||||
fmt.Fprintf(v, "%s", highlightTerms(buf, dp.terms))
|
||||
return countLines(msg) + countLines(buf)
|
||||
}
|
||||
|
||||
func scrollView(v *gocui.View, dy, lineCnt int) {
|
||||
func (t *TermUI) scrollView(v *gocui.View, dy int) {
|
||||
// Get the size and position of the view.
|
||||
_, height := v.Size()
|
||||
ox1, oy1 := v.Origin()
|
||||
cx1, cy1 := v.Cursor()
|
||||
lineCnt := len(t.lines)
|
||||
_, windowHeight := v.Size()
|
||||
ox, oy := v.Origin()
|
||||
cx, cy := v.Cursor()
|
||||
|
||||
// maxCy will either be the height of the screen - 1, or in the case that
|
||||
// the there aren't enough lines to fill the screen, it will be the
|
||||
// lineCnt - origin
|
||||
newCy := cy1 + dy
|
||||
maxCy := math.MinInt(lineCnt-oy1, height-1)
|
||||
newCy := cy + dy
|
||||
maxCy := math.MinInt(lineCnt-oy, windowHeight-1)
|
||||
|
||||
// If the newCy doesn't require scrolling, then just move the cursor.
|
||||
if newCy >= 0 && newCy < maxCy {
|
||||
v.MoveCursor(cx1, dy, false)
|
||||
v.MoveCursor(cx, dy, false)
|
||||
return
|
||||
}
|
||||
|
||||
// If the cursor is already at the bottom of the screen and there are no
|
||||
// lines left to scroll up, then we're at the bottom.
|
||||
if newCy >= maxCy && oy1 >= lineCnt-height {
|
||||
if newCy >= maxCy && oy >= lineCnt-windowHeight {
|
||||
// Set autoscroll to normal again.
|
||||
v.Autoscroll = true
|
||||
} else {
|
||||
// The cursor is already at the bottom or top of the screen so scroll
|
||||
// the text
|
||||
v.Autoscroll = false
|
||||
v.SetOrigin(ox1, oy1+dy)
|
||||
v.SetOrigin(ox, oy+dy)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,30 +233,45 @@ func quitWithStack(_ *gocui.Gui, _ *gocui.View) error {
|
||||
return gocui.ErrQuit
|
||||
}
|
||||
|
||||
func arrowUp(_ *gocui.Gui, v *gocui.View) error {
|
||||
lineCnt := countLines(viewBuffer(v))
|
||||
if _, oy := v.Origin(); oy == 0 {
|
||||
lineCnt = prependMessages(v)
|
||||
func arrowUp(t *TermUI) func(*gocui.Gui, *gocui.View) error {
|
||||
return func(_ *gocui.Gui, v *gocui.View) error {
|
||||
lineCnt := len(t.lines)
|
||||
ox, oy := v.Origin()
|
||||
if oy == 0 {
|
||||
var ok bool
|
||||
t.lines, ok = t.dp.Prepend(t.lines, linestofetch)
|
||||
if ok {
|
||||
v.Clear()
|
||||
for _, s := range t.lines {
|
||||
fmt.Fprintf(v, "%s\n", s)
|
||||
}
|
||||
c1 := len(t.lines)
|
||||
v.SetOrigin(ox, c1-lineCnt)
|
||||
}
|
||||
}
|
||||
t.scrollView(v, -1)
|
||||
return nil
|
||||
}
|
||||
scrollView(v, -1, lineCnt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func arrowDown(_ *gocui.Gui, v *gocui.View) error {
|
||||
lineCnt := countLines(viewBuffer(v))
|
||||
scrollView(v, 1, lineCnt)
|
||||
return nil
|
||||
func arrowDown(t *TermUI) func(*gocui.Gui, *gocui.View) error {
|
||||
return func(_ *gocui.Gui, v *gocui.View) error {
|
||||
t.scrollView(v, 1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func debugInfo(g *gocui.Gui, _ *gocui.View) error {
|
||||
msgView, _ := g.View(messageView)
|
||||
w, h := msgView.Size()
|
||||
dbg.Debug("info, window size:(%d, %d), lineCnt: %d", w, h, countLines(viewBuffer(msgView)))
|
||||
cx, cy := msgView.Cursor()
|
||||
ox, oy := msgView.Origin()
|
||||
dbg.Debug("info, origin: (%d,%d), cursor: (%d,%d)", ox, oy, cx, cy)
|
||||
dbg.Debug("info, view buffer:\n%s", highlightTerms(viewBuffer(msgView), dp.terms))
|
||||
return nil
|
||||
func debugInfo(t *TermUI) func(*gocui.Gui, *gocui.View) error {
|
||||
return func(g *gocui.Gui, _ *gocui.View) error {
|
||||
msgView, _ := g.View(messageView)
|
||||
w, h := msgView.Size()
|
||||
dbg.Debug("info, window size:(%d, %d), lineCnt: %d", w, h, len(t.lines))
|
||||
cx, cy := msgView.Cursor()
|
||||
ox, oy := msgView.Origin()
|
||||
dbg.Debug("info, origin: (%d,%d), cursor: (%d,%d)", ox, oy, cx, cy)
|
||||
dbg.Debug("info, view buffer:\n%s", highlightTerms(viewBuffer(msgView), t.dp.terms))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func viewBuffer(v *gocui.View) string {
|
||||
@@ -290,10 +282,6 @@ func viewBuffer(v *gocui.View) string {
|
||||
return buf
|
||||
}
|
||||
|
||||
func countLines(s string) int {
|
||||
return strings.Count(s, "\n")
|
||||
}
|
||||
|
||||
func nextView(g *gocui.Gui, v *gocui.View) (err error) {
|
||||
nextName := nextViewName(v.Name())
|
||||
if _, err = g.SetCurrentView(nextName); err != nil {
|
||||
@@ -312,15 +300,15 @@ func nextViewName(currentView string) string {
|
||||
return viewNames[0]
|
||||
}
|
||||
|
||||
func textScrolledToEnd(g *gocui.Gui) bool {
|
||||
v, err := g.View(messageView)
|
||||
func (t *TermUI) textScrolledToEnd() bool {
|
||||
v, err := t.Gui.View(messageView)
|
||||
if err != nil {
|
||||
// doubt this will ever happen, if it does just assume we're at bottom
|
||||
return true
|
||||
}
|
||||
_, oy := v.Origin()
|
||||
_, h := v.Size()
|
||||
lc := countLines(viewBuffer(v))
|
||||
lc := len(t.lines)
|
||||
dbg.Debug("textScrolledToEnd, oy: %d, h: %d, lc: %d, lc-oy: %d, res: %t", oy, h, lc, lc-oy, lc-oy <= h)
|
||||
return lc-oy <= h
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func runClient(cInfo lib.ClientInfo) {
|
||||
t.ResetAuthors(ds)
|
||||
t.UpdateMessages(ds, nil, nil)
|
||||
|
||||
go lib.ProcessChatEvents(node, ds, events, &t, cInfo)
|
||||
go lib.ProcessChatEvents(node, ds, events, t, cInfo)
|
||||
go lib.ReceiveMessages(node, events, cInfo)
|
||||
|
||||
if err := t.Gui.MainLoop(); err != nil && err != gocui.ErrQuit {
|
||||
|
||||
Reference in New Issue
Block a user