diff --git a/go.mod b/go.mod index 054d0ffe..1c77a6d2 100644 --- a/go.mod +++ b/go.mod @@ -14,12 +14,12 @@ require ( github.com/noborus/guesswidth v0.3.4 github.com/pierrec/lz4 v2.6.1+incompatible github.com/rivo/uniseg v0.4.4 - github.com/spf13/cobra v1.7.0 + github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.17.0 github.com/ulikunitz/xz v0.5.11 golang.org/x/exp v0.0.0-20231006140011-7918f672742d - golang.org/x/sync v0.4.0 + golang.org/x/sync v0.5.0 golang.org/x/term v0.13.0 ) @@ -39,8 +39,8 @@ require ( github.com/spf13/cast v1.5.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 87c61848..61d04066 100644 --- a/go.sum +++ b/go.sum @@ -50,7 +50,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -192,8 +192,8 @@ github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= @@ -322,8 +322,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -365,8 +365,8 @@ golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -384,8 +384,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/oviewer/action.go b/oviewer/action.go index feb8888e..43ba58ca 100644 --- a/oviewer/action.go +++ b/oviewer/action.go @@ -160,10 +160,7 @@ func (root *Root) watchControl() { m.tickerDone <- struct{}{} return case <-m.ticker.C: - ev := &eventReload{} - ev.SetEventNow() - ev.m = m - root.postEvent(ev) + root.sendReload(m) } } }() @@ -549,6 +546,10 @@ func jumpPosition(height int, str string) (int, bool) { // numbers (1), returns dot.number for percentages (.5) = 50%, // and returns the % after the number for percentages (50%). return. func calculatePosition(length int, str string) float64 { + if len(str) == 0 || str == "0" { + return 0 + } + var p float64 = 0 if strings.HasPrefix(str, ".") { str = strings.TrimLeft(str, ".") @@ -573,10 +574,8 @@ func calculatePosition(length int, str string) float64 { num, err := strconv.ParseFloat(str, 64) if err != nil { - log.Println(err) return 0 } - return num } diff --git a/oviewer/event.go b/oviewer/event.go index f00ca2ff..c7669a76 100644 --- a/oviewer/event.go +++ b/oviewer/event.go @@ -3,6 +3,7 @@ package oviewer import ( "context" "log" + "strconv" "sync/atomic" "time" @@ -98,6 +99,32 @@ func (root *Root) eventLoop(ctx context.Context, quitChan chan<- struct{}) { } } +// sendGoto fires an eventGoto event that moves to the specified line. +func (root *Root) sendGoto(num int) { + ev := &eventGoto{} + ev.value = strconv.Itoa(num) + ev.SetEventNow() + root.postEvent(ev) +} + +// MoveLine fires an eventGoto event that moves to the specified line. +func (root *Root) MoveLine(num int) { + if !root.checkScreen() { + return + } + root.sendGoto(num) +} + +// MoveTop fires the event of moving to top. +func (root *Root) MoveTop() { + root.MoveLine(root.Doc.BufStartNum()) +} + +// MoveBottom fires the event of moving to bottom. +func (root *Root) MoveBottom() { + root.MoveLine(root.Doc.BufEndNum()) +} + // everyUpdate is called every time before running the event. func (root *Root) everyUpdate() { // If tmpLN is set, set top position to position from bottom. @@ -294,15 +321,15 @@ type eventReload struct { // Reload fires the eventReload event. func (root *Root) Reload() { - root.sendReload() + root.sendReload(root.Doc) } -func (root *Root) sendReload() { +func (root *Root) sendReload(m *Document) { if !root.checkScreen() { return } ev := &eventReload{} - ev.m = root.Doc + ev.m = m ev.SetEventNow() root.postEvent(ev) } diff --git a/oviewer/mouse.go b/oviewer/mouse.go index 87934e6e..d48abe7a 100644 --- a/oviewer/mouse.go +++ b/oviewer/mouse.go @@ -11,6 +11,9 @@ import ( "github.com/mattn/go-runewidth" ) +// WheelScrollNum is the number of lines to scroll with the mouse wheel. +var WheelScrollNum = 2 + // mouseEvent handles mouse events. func (root *Root) mouseEvent(ev *tcell.EventMouse) { button := ev.Buttons() @@ -60,13 +63,13 @@ func (root *Root) mouseEvent(ev *tcell.EventMouse) { // wheelUp moves the mouse wheel up. func (root *Root) wheelUp() { root.setMessage("") - root.moveUp(2) + root.moveUp(WheelScrollNum) } // wheelDown moves the mouse wheel down. func (root *Root) wheelDown() { root.setMessage("") - root.moveDown(2) + root.moveDown(WheelScrollNum) } func (root *Root) wheelRight() { diff --git a/oviewer/move.go b/oviewer/move.go index b58690e1..4e395646 100644 --- a/oviewer/move.go +++ b/oviewer/move.go @@ -1,40 +1,11 @@ package oviewer -import ( - "strconv" -) +// Called by event key to change document position. -// bottomMargin is the margin of the bottom line when specifying section +// bottomMargin is the margin of the bottom line when specifying section. const bottomMargin = 2 -// MoveLine fires an eventGoto event that moves to the specified line. -func (root *Root) MoveLine(num int) { - if !root.checkScreen() { - return - } - root.sendGoto(num) -} - -// sendGoto fires an eventGoto event that moves to the specified line. -func (root *Root) sendGoto(num int) { - ev := &eventGoto{} - ev.value = strconv.Itoa(num) - ev.SetEventNow() - root.postEvent(ev) -} - -// MoveTop fires the event of moving to top. -func (root *Root) MoveTop() { - root.MoveLine(root.Doc.BufStartNum()) -} - -// MoveBottom fires the event of moving to bottom. -func (root *Root) MoveBottom() { - root.MoveLine(root.Doc.BufEndNum()) -} - // Go to the top line. -// Called from a EventKey. func (root *Root) moveTop() { root.resetSelect() defer root.releaseEventBuffer() @@ -43,7 +14,6 @@ func (root *Root) moveTop() { } // Go to the bottom line. -// Called from a EventKey. func (root *Root) moveBottom() { root.resetSelect() defer root.releaseEventBuffer() @@ -52,7 +22,6 @@ func (root *Root) moveBottom() { } // Move up one screen. -// Called from a EventKey. func (root *Root) movePgUp() { root.resetSelect() defer root.releaseEventBuffer() @@ -61,7 +30,6 @@ func (root *Root) movePgUp() { } // Moves down one screen. -// Called from a EventKey. func (root *Root) movePgDn() { root.resetSelect() defer root.releaseEventBuffer() @@ -70,7 +38,6 @@ func (root *Root) movePgDn() { } // Moves up half a screen. -// Called from a EventKey. func (root *Root) moveHfUp() { root.resetSelect() defer root.releaseEventBuffer() @@ -79,7 +46,6 @@ func (root *Root) moveHfUp() { } // Moves down half a screen. -// Called from a EventKey. func (root *Root) moveHfDn() { root.resetSelect() defer root.releaseEventBuffer() @@ -88,13 +54,11 @@ func (root *Root) moveHfDn() { } // Move up one line. -// Called from a EventKey. func (root *Root) moveUpOne() { root.moveUp(1) } // Move down one line. -// Called from a EventKey. func (root *Root) moveDownOne() { root.moveDown(1) } @@ -146,25 +110,21 @@ func (root *Root) lastSection() { } // Move to the left. -// Called from a EventKey. func (root *Root) moveLeftOne() { root.moveLeft(1) } // Move to the right. -// Called from a EventKey. func (root *Root) moveRightOne() { root.moveRight(1) } // Move to the width of the screen to the left. -// Called from a EventKey. func (root *Root) moveWidthLeft() { root.moveLeft(root.Doc.HScrollWidthNum) } // Move to the width of the screen to the right. -// Called from a EventKey. func (root *Root) moveWidthRight() { root.moveRight(root.Doc.HScrollWidthNum) } @@ -194,7 +154,6 @@ func (root *Root) moveRight(n int) { } // Move to the left by half a screen. -// Called from a EventKey. func (root *Root) moveHfLeft() { root.resetSelect() defer root.releaseEventBuffer() @@ -204,7 +163,6 @@ func (root *Root) moveHfLeft() { } // Move to the right by half a screen. -// Called from a EventKey. func (root *Root) moveHfRight() { root.resetSelect() defer root.releaseEventBuffer() @@ -247,72 +205,3 @@ func (root *Root) moveColumnRight(n int) { root.debugMessage(err.Error()) } } - -// searchGoTo moves to the specified line and position after searching. -// Go to the specified line +root.Doc.JumpTarget Go to. -// If the search term is off screen, move until the search term is visible. -func (m *Document) searchGoTo(lN int, x int) { - m.searchGoX(x) - - m.topLN = lN - m.firstLine() - m.moveYUp(m.jumpTargetNum) -} - -// Bottom line of jumpTarget when specifying a section -func (m *Document) bottomJumpTarget() int { - return m.statusPos - bottomMargin -} - -// searchGoSection will go to the section with the matching term after searching. -// Move the JumpTarget so that it can be seen from the beginning of the section. -func (m *Document) searchGoSection(lN int, x int) { - m.searchGoX(x) - - sN, err := m.prevSection(lN) - if err != nil { - sN = 0 - } - if m.SectionHeader { - sN = (sN - m.firstLine() + m.sectionHeaderNum) + m.SectionStartPosition - sN = max(sN, m.BufStartNum()) - } - y := 0 - if sN < m.firstLine() { - // topLN is negative if the section is less than header + skip. - m.topLN = sN - m.firstLine() - } else { - m.topLN = sN - sN += m.firstLine() - } - - if !m.WrapMode { - y = lN - sN - } else { - for n := sN; n < lN; n++ { - listX := m.leftMostX(n) - y += len(listX) - } - } - - if m.bottomJumpTarget() > y+m.headerLen { - m.jumpTargetNum = y - return - } - - m.jumpTargetNum = m.bottomJumpTarget() - (m.headerLen + 1) - m.moveYDown(y - m.jumpTargetNum) -} - -// searchGoX moves to the specified x position. -func (m *Document) searchGoX(x int) { - m.topLX = 0 - // If the search term is outside the height of the screen when in WrapMode. - if nTh := x / m.width; nTh > m.height { - m.topLX = nTh * m.width - } - - // If the search term is outside the width of the screen when in NoWrapMode. - if x < m.x || x > m.x+m.width-1 { - m.x = x - } -} diff --git a/oviewer/oviewer.go b/oviewer/oviewer.go index 44e4617e..78c088f8 100644 --- a/oviewer/oviewer.go +++ b/oviewer/oviewer.go @@ -888,10 +888,13 @@ func (root *Root) prepareView() { // Do not allow size 0. root.scr.vWidth = max(root.scr.vWidth, 1) root.scr.vHeight = max(root.scr.vHeight, 1) + root.Doc.statusPos = root.scr.vHeight - statusLine + num := int(math.Round(calculatePosition(root.scr.vWidth, root.Doc.HScrollWidth))) root.Doc.HScrollWidthNum = max(num, 1) - root.scr.numbers = make([]LineNumber, root.scr.vHeight+1) - root.Doc.statusPos = root.scr.vHeight - statusLine + if len(root.scr.numbers) != root.scr.vHeight+1 { + root.scr.numbers = make([]LineNumber, root.scr.vHeight+1) + } } // docSmall returns with bool whether the file to display fits on the screen. diff --git a/oviewer/search_move.go b/oviewer/search_move.go new file mode 100644 index 00000000..36965052 --- /dev/null +++ b/oviewer/search_move.go @@ -0,0 +1,70 @@ +package oviewer + +// searchGoTo moves to the specified line and position after searching. +// Go to the specified line +root.Doc.JumpTarget Go to. +// If the search term is off screen, move until the search term is visible. +func (m *Document) searchGoTo(lN int, x int) { + m.searchGoX(x) + + m.topLN = lN - m.firstLine() + m.moveYUp(m.jumpTargetNum) +} + +// Bottom line of jumpTarget when specifying a section +func (m *Document) bottomJumpTarget() int { + return m.statusPos - bottomMargin +} + +// searchGoSection will go to the section with the matching term after searching. +// Move the JumpTarget so that it can be seen from the beginning of the section. +func (m *Document) searchGoSection(lN int, x int) { + m.searchGoX(x) + + sN, err := m.prevSection(lN) + if err != nil { + sN = 0 + } + if m.SectionHeader { + sN = (sN - m.firstLine() + m.sectionHeaderNum) + m.SectionStartPosition + sN = max(sN, m.BufStartNum()) + } + y := 0 + if sN < m.firstLine() { + // topLN is negative if the section is less than header + skip. + m.topLN = sN - m.firstLine() + } else { + m.topLN = sN + sN += m.firstLine() + } + + if !m.WrapMode { + y = lN - sN + } else { + for n := sN; n < lN; n++ { + listX := m.leftMostX(n) + y += len(listX) + } + } + + if m.bottomJumpTarget() > y+m.headerLen { + m.jumpTargetNum = y + return + } + + m.jumpTargetNum = m.bottomJumpTarget() - (m.headerLen + 1) + m.moveYDown(y - m.jumpTargetNum) +} + +// searchGoX moves to the specified x position. +func (m *Document) searchGoX(x int) { + m.topLX = 0 + // If the search term is outside the height of the screen when in WrapMode. + if nTh := x / m.width; nTh > m.height { + m.topLX = nTh * m.width + } + + // If the search term is outside the width of the screen when in NoWrapMode. + if x < m.x || x > m.x+m.width-1 { + m.x = x + } +}