diff --git a/internal/oviewer/keybind.go b/internal/oviewer/keybind.go index b399d96a..d9b37d68 100644 --- a/internal/oviewer/keybind.go +++ b/internal/oviewer/keybind.go @@ -1,8 +1,6 @@ package oviewer import ( - "strconv" - "github.com/gdamore/tcell" ) @@ -25,6 +23,28 @@ func (root *Root) HandleEvent(ev *tcell.EventKey) bool { } } +func (root *Root) inputEvent(ev *tcell.EventKey, fn func()) bool { + switch ev.Key() { + case tcell.KeyEscape: + root.mode = normal + case tcell.KeyEnter: + fn() + root.mode = normal + case tcell.KeyBackspace, tcell.KeyBackspace2: + if len(root.input) > 0 { + r := []rune(root.input) + root.input = string(r[:len(r)-1]) + } + case tcell.KeyTAB: + root.input += "\t" + case tcell.KeyCtrlA: + root.CaseSensitive = !root.CaseSensitive + case tcell.KeyRune: + root.input += string(ev.Rune()) + } + return true +} + func (root *Root) defaultEvent(ev *tcell.EventKey) bool { switch ev.Key() { case tcell.KeyEscape, tcell.KeyCtrlC: @@ -81,22 +101,22 @@ func (root *Root) defaultEvent(ev *tcell.EventKey) bool { root.Quit() return true case 'W', 'w': - root.keyWrap() + root.toggleWrap() return true case '?': root.input = "" - root.keyPrevious() + root.setMode(previous) return true case 'c': - root.keyColumnMode() + root.toggleColumnMode() return true case 'd': root.input = "" - root.keyDelimiter() + root.setMode(delimiter) return true case '/': root.input = "" - root.keySearch() + root.setMode(search) return true case 'n': root.NextSearch() @@ -106,142 +126,16 @@ func (root *Root) defaultEvent(ev *tcell.EventKey) bool { return true case 'g': root.input = "" - root.keyGoLine() + root.setMode(goline) return true case 'H': root.input = "" - root.keyHeader() + root.setMode(header) return true case 'C': - root.keyAlternateRows() + root.toggleAlternateRows() return true } } return true } - -func (root *Root) keyWrap() { - if root.WrapMode { - root.WrapMode = false - } else { - root.WrapMode = true - root.Model.x = 0 - } - root.setWrapHeaderLen() -} - -func (root *Root) keyColumnMode() { - if root.ColumnMode { - root.ColumnMode = false - } else { - root.ColumnMode = true - } -} - -func (root *Root) keyAlternateRows() { - root.Model.ClearCache() - if root.AlternateRows { - root.AlternateRows = false - } else { - root.AlternateRows = true - } -} - -func (root *Root) keySearch() { - root.mode = search -} - -func (root *Root) keyDelimiter() { - root.mode = delimiter -} - -func (root *Root) keyPrevious() { - root.mode = previous -} - -func (root *Root) keyGoLine() { - root.mode = goline -} - -func (root *Root) keyHeader() { - root.mode = header -} - -func (root *Root) inputEvent(ev *tcell.EventKey, fn func()) bool { - switch ev.Key() { - case tcell.KeyEscape: - root.mode = normal - case tcell.KeyEnter: - fn() - root.mode = normal - case tcell.KeyBackspace, tcell.KeyBackspace2: - if len(root.input) > 0 { - r := []rune(root.input) - root.input = string(r[:len(r)-1]) - } - case tcell.KeyTAB: - root.input += "\t" - case tcell.KeyCtrlA: - root.CaseSensitive = !root.CaseSensitive - case tcell.KeyRune: - root.input += string(ev.Rune()) - } - return true -} - -// GoLine will move to the specified line. -func (root *Root) GoLine() { - lineNum, err := strconv.Atoi(root.input) - if err != nil { - return - } - root.input = "" - root.moveNum(lineNum - root.Header) -} - -// SetHeader sets the number of lines in the header. -func (root *Root) SetHeader() { - line, _ := strconv.Atoi(root.input) - if line >= 0 && line <= root.Model.vHight-1 { - if root.Header != line { - root.Header = line - root.setWrapHeaderLen() - root.Model.ClearCache() - } - } - root.input = "" -} - -// SetDelimiter sets the delimiter string. -func (root *Root) SetDelimiter() { - root.ColumnDelimiter = root.input - root.input = "" -} - -// Search is a forward search. -func (root *Root) Search() { - root.postSearch(root.search(root.Model.lineNum)) -} - -// NextSearch will re-run the forward search. -func (root *Root) NextSearch() { - root.postSearch(root.search(root.Model.lineNum + root.Header + 1)) -} - -// BackSearch reverse search. -func (root *Root) BackSearch() { - root.postSearch(root.backSearch(root.Model.lineNum)) -} - -// NextBackSearch will re-run the reverse search. -func (root *Root) NextBackSearch() { - root.postSearch(root.backSearch(root.Model.lineNum + root.Header - 1)) -} - -func (root *Root) postSearch(lineNum int, err error) { - if err != nil { - root.message = err.Error() - return - } - root.moveNum(lineNum - root.Header) -} diff --git a/internal/oviewer/oviewer.go b/internal/oviewer/oviewer.go index 6f1f0b5e..32c2f8d0 100644 --- a/internal/oviewer/oviewer.go +++ b/internal/oviewer/oviewer.go @@ -6,7 +6,9 @@ import ( "io" "os" "os/signal" + "strconv" "syscall" + "time" "github.com/gdamore/tcell" "golang.org/x/crypto/ssh/terminal" @@ -57,6 +59,19 @@ const ( //Debug represents whether to enable the debug output. var Debug bool +// New returns the entire structure of oviewer. +func New() *Root { + root := &Root{} + root.Model = NewModel() + + root.minStartPos = -10 + root.HeaderStyle = tcell.StyleDefault.Bold(true) + root.ColorAlternate = tcell.ColorGray + root.ColumnDelimiter = "" + root.columnNum = 0 + return root +} + // PrepareView prepares when the screen size is changed. func (root *Root) PrepareView() { m := root.Model @@ -99,6 +114,66 @@ func (root *Root) bottomLineNum(num int) int { return num } +func (root *Root) toggleWrap() { + if root.WrapMode { + root.WrapMode = false + } else { + root.WrapMode = true + root.Model.x = 0 + } + root.setWrapHeaderLen() +} + +func (root *Root) toggleColumnMode() { + if root.ColumnMode { + root.ColumnMode = false + } else { + root.ColumnMode = true + } +} + +func (root *Root) toggleAlternateRows() { + root.Model.ClearCache() + if root.AlternateRows { + root.AlternateRows = false + } else { + root.AlternateRows = true + } +} + +func (root *Root) setMode(mode Mode) { + root.mode = mode +} + +// GoLine will move to the specified line. +func (root *Root) GoLine() { + lineNum, err := strconv.Atoi(root.input) + if err != nil { + return + } + root.input = "" + root.moveNum(lineNum - root.Header) +} + +// SetHeader sets the number of lines in the header. +func (root *Root) SetHeader() { + line, _ := strconv.Atoi(root.input) + if line >= 0 && line <= root.Model.vHight-1 { + if root.Header != line { + root.Header = line + root.setWrapHeaderLen() + root.Model.ClearCache() + } + } + root.input = "" +} + +// SetDelimiter sets the delimiter string. +func (root *Root) SetDelimiter() { + root.ColumnDelimiter = root.input + root.input = "" +} + type eventAppQuit struct { tcell.EventTime } @@ -110,6 +185,17 @@ func (root *Root) Quit() { go func() { root.Screen.PostEventWait(ev) }() } +type eventTimer struct { + tcell.EventTime +} + +// runOnTime runs at time. +func (root *Root) runOnTime() { + ev := &eventTimer{} + ev.SetEventNow() + go func() { root.Screen.PostEventWait(ev) }() +} + // Resize is a wrapper function that calls sync. func (root *Root) Resize() { root.Sync() @@ -121,14 +207,30 @@ func (root *Root) Sync() { root.Draw() } +func (root *Root) countTimer() { + timer := time.NewTicker(time.Millisecond * 500) +loop: + for { + <-timer.C + root.runOnTime() + if root.Model.eof { + break loop + } + } + timer.Stop() +} + // main is manages and executes events in the main routine. func (root *Root) main() { screen := root.Screen + go root.countTimer() loop: for { root.Draw() ev := screen.PollEvent() switch ev := ev.(type) { + case *eventTimer: + root.statusDraw() case *eventAppQuit: break loop case *tcell.EventKey: @@ -238,16 +340,3 @@ func (root *Root) WriteOriginal() { fmt.Println(m.GetLine(m.lineNum + i)) } } - -// New returns the entire structure of oviewer. -func New() *Root { - root := &Root{} - root.Model = NewModel() - - root.minStartPos = -10 - root.HeaderStyle = tcell.StyleDefault.Bold(true) - root.ColorAlternate = tcell.ColorGray - root.ColumnDelimiter = "" - root.columnNum = 0 - return root -} diff --git a/internal/oviewer/search.go b/internal/oviewer/search.go index 680c527d..b4960746 100644 --- a/internal/oviewer/search.go +++ b/internal/oviewer/search.go @@ -5,6 +5,34 @@ import ( "strings" ) +// Search is a forward search. +func (root *Root) Search() { + root.postSearch(root.search(root.Model.lineNum)) +} + +// NextSearch will re-run the forward search. +func (root *Root) NextSearch() { + root.postSearch(root.search(root.Model.lineNum + root.Header + 1)) +} + +// BackSearch reverse search. +func (root *Root) BackSearch() { + root.postSearch(root.backSearch(root.Model.lineNum)) +} + +// NextBackSearch will re-run the reverse search. +func (root *Root) NextBackSearch() { + root.postSearch(root.backSearch(root.Model.lineNum + root.Header - 1)) +} + +func (root *Root) postSearch(lineNum int, err error) { + if err != nil { + root.message = err.Error() + return + } + root.moveNum(lineNum - root.Header) +} + func (root *Root) search(num int) (int, error) { for n := num; n < root.Model.BufEndNum(); n++ { if contains(root.Model.buffer[n], root.input, root.CaseSensitive) { @@ -35,7 +63,7 @@ type rangePos struct { end int } -func rangePosition(s string, substr string, number int) rangePos { +func rangePosition(s, substr string, number int) rangePos { r := rangePos{0, 0} i := 0 @@ -70,7 +98,7 @@ func rangePosition(s string, substr string, number int) rangePos { return r } -func searchPosition(s string, substr string, caseSensitive bool) []rangePos { +func searchPosition(s, substr string, caseSensitive bool) []rangePos { var pos []rangePos if !caseSensitive { s = strings.ToLower(s)