From 8fb912e2617591203f51e018f31df8258f4965cf Mon Sep 17 00:00:00 2001 From: Johan Walles Date: Tue, 9 Jan 2024 14:52:00 +0100 Subject: [PATCH 1/6] Initial marks commit --- m/pager.go | 6 ++++-- m/pagermode-viewing.go | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/m/pager.go b/m/pager.go index fe5626b..df711f9 100644 --- a/m/pager.go +++ b/m/pager.go @@ -122,12 +122,14 @@ Moving around * Arrow keys * Alt key plus left / right arrow steps one column at a time * Left / right can be used to hide / show line numbers +* Home and End for start / end of the document +* 'g' for going to a specific line number +* ' (single quote) sets a mark +* 'm' jumps to the mark * CTRL-p moves to the previous line * CTRL-n moves to the next line -* 'g' for going to a specific line number * PageUp / 'b' and PageDown / 'f' * SPACE moves down a page -* Home and End for start / end of the document * < / 'gg' to go to the start of the document * > / 'G' to go to the end of the document * 'h', 'l' for left and right (as in vim) diff --git a/m/pagermode-viewing.go b/m/pagermode-viewing.go index bcdf06c..bd7e730 100644 --- a/m/pagermode-viewing.go +++ b/m/pagermode-viewing.go @@ -162,6 +162,14 @@ func (m PagerModeViewing) onRune(char rune) { case 'p', 'N': p.scrollToPreviousSearchHit() + case 'm': + p.mode = PagerModeMark{pager: p} + p.TargetLineNumber = nil + + case '\'': + p.mode = PagerModeJumpToMark{pager: p} + p.TargetLineNumber = nil + case 'w': p.WrapLongLines = !p.WrapLongLines From ab10a82b6cdc766c99e6d79939faac74b6bd9d0f Mon Sep 17 00:00:00 2001 From: Johan Walles Date: Tue, 9 Jan 2024 15:02:43 +0100 Subject: [PATCH 2/6] Add a mark setting mode --- m/pager.go | 5 +++++ m/pagermode-mark.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 m/pagermode-mark.go diff --git a/m/pager.go b/m/pager.go index df711f9..ab60855 100644 --- a/m/pager.go +++ b/m/pager.go @@ -97,6 +97,11 @@ type Pager struct { // Length of the longest line displayed. This is used for limiting scrolling to the right. longestLineLength int + + // Bookmarks that you can come back to. + // + // Ref: https://github.com/walles/moar/issues/175 + marks map[rune]scrollPosition } type _PreHelpState struct { diff --git a/m/pagermode-mark.go b/m/pagermode-mark.go new file mode 100644 index 0000000..6cfd87a --- /dev/null +++ b/m/pagermode-mark.go @@ -0,0 +1,43 @@ +package m + +import "github.com/walles/moar/twin" + +type PagerModeMark struct { + pager *Pager +} + +func (m PagerModeMark) drawFooter(statusText string, spinner string) { + p := m.pager + + _, height := p.screen.Size() + + pos := 0 + for _, token := range "Press any key to label your mark: " + p.gotoLineString { + p.screen.SetCell(pos, height-1, twin.NewCell(token, twin.StyleDefault)) + pos++ + } + + // Add a cursor + p.screen.SetCell(pos, height-1, twin.NewCell(' ', twin.StyleDefault.WithAttr(twin.AttrReverse))) +} + +func (m PagerModeMark) onKey(key twin.KeyCode) { + p := m.pager + + switch key { + case twin.KeyEnter, twin.KeyEscape: + // Never mind I + p.mode = PagerModeViewing{pager: p} + + default: + // Never mind II + p.mode = PagerModeViewing{pager: p} + p.mode.onKey(key) + } +} + +func (m PagerModeMark) onRune(char rune) { + m.pager.marks[char] = m.pager.scrollPosition + + m.pager.mode = PagerModeViewing{pager: m.pager} +} From 4867b2c2edca8476be631dcbb560c407960688dd Mon Sep 17 00:00:00 2001 From: Johan Walles Date: Tue, 9 Jan 2024 15:26:16 +0100 Subject: [PATCH 3/6] Implement jumping to marks --- go.mod | 5 ++- go.sum | 2 + m/pagermode-jump-to-mark.go | 81 +++++++++++++++++++++++++++++++++++++ m/pagermode-mark.go | 5 ++- 4 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 m/pagermode-jump-to-mark.go diff --git a/go.mod b/go.mod index 7a8ae10..c3aeae3 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,10 @@ go 1.20 require ( github.com/alecthomas/chroma/v2 v2.12.0 github.com/google/go-cmp v0.5.9 + github.com/klauspost/compress v1.17.4 github.com/sirupsen/logrus v1.8.1 + github.com/ulikunitz/xz v0.5.11 + golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc golang.org/x/sys v0.1.0 golang.org/x/term v0.0.0-20210503060354-a79de5458b56 gotest.tools/v3 v3.3.0 @@ -13,7 +16,5 @@ require ( require ( github.com/dlclark/regexp2 v1.10.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect github.com/stretchr/testify v1.7.0 // indirect - github.com/ulikunitz/xz v0.5.11 // indirect ) diff --git a/go.sum b/go.sum index b7910e1..26d576c 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= +golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= diff --git a/m/pagermode-jump-to-mark.go b/m/pagermode-jump-to-mark.go new file mode 100644 index 0000000..c7867f8 --- /dev/null +++ b/m/pagermode-jump-to-mark.go @@ -0,0 +1,81 @@ +package m + +import ( + "sort" + + "github.com/walles/moar/twin" + "golang.org/x/exp/maps" +) + +type PagerModeJumpToMark struct { + pager *Pager +} + +func (m PagerModeJumpToMark) drawFooter(_ string, _ string) { + p := m.pager + + _, height := p.screen.Size() + + pos := 0 + for _, token := range m.getMarkPrompt() { + p.screen.SetCell(pos, height-1, twin.NewCell(token, twin.StyleDefault)) + pos++ + } + + // Add a cursor + p.screen.SetCell(pos, height-1, twin.NewCell(' ', twin.StyleDefault.WithAttr(twin.AttrReverse))) +} + +func (m PagerModeJumpToMark) getMarkPrompt() string { + // Special case having zero, one or multiple marks + if len(m.pager.marks) == 0 { + return "Press \"m\" to set your first mark!" + } + + if len(m.pager.marks) == 1 { + for key := range m.pager.marks { + return "Press \"" + string(key) + "\" to jump to your mark!" + } + } + + // Multiple marks, list them + marks := maps.Keys(m.pager.marks) + sort.Slice(marks, func(i, j int) bool { + return marks[i] < marks[j] + }) + + prompt := "Press a key to jump to your mark: " + for i, mark := range marks { + if i > 0 { + prompt += ", " + } + prompt += string(mark) + } + + return prompt +} + +func (m PagerModeJumpToMark) onKey(key twin.KeyCode) { + p := m.pager + + switch key { + case twin.KeyEnter, twin.KeyEscape: + // Never mind I + p.mode = PagerModeViewing{pager: p} + + default: + // Never mind II + p.mode = PagerModeViewing{pager: p} + p.mode.onKey(key) + } +} + +func (m PagerModeJumpToMark) onRune(char rune) { + destination, ok := m.pager.marks[char] + if ok { + m.pager.scrollPosition = destination + } + + //nolint:gosimple // The linter's advice is just wrong here + m.pager.mode = PagerModeViewing{pager: m.pager} +} diff --git a/m/pagermode-mark.go b/m/pagermode-mark.go index 6cfd87a..299604f 100644 --- a/m/pagermode-mark.go +++ b/m/pagermode-mark.go @@ -6,13 +6,13 @@ type PagerModeMark struct { pager *Pager } -func (m PagerModeMark) drawFooter(statusText string, spinner string) { +func (m PagerModeMark) drawFooter(_ string, _ string) { p := m.pager _, height := p.screen.Size() pos := 0 - for _, token := range "Press any key to label your mark: " + p.gotoLineString { + for _, token := range "Press any key to label your mark: " { p.screen.SetCell(pos, height-1, twin.NewCell(token, twin.StyleDefault)) pos++ } @@ -39,5 +39,6 @@ func (m PagerModeMark) onKey(key twin.KeyCode) { func (m PagerModeMark) onRune(char rune) { m.pager.marks[char] = m.pager.scrollPosition + //nolint:gosimple // The linter's advice is just wrong here m.pager.mode = PagerModeViewing{pager: m.pager} } From a9fbc3f68173f34d79eb6e5c27b222c33fad5658 Mon Sep 17 00:00:00 2001 From: Johan Walles Date: Tue, 9 Jan 2024 15:37:42 +0100 Subject: [PATCH 4/6] Fixups after testing --- m/pager.go | 2 ++ m/pagermode-jump-to-mark.go | 15 +++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/m/pager.go b/m/pager.go index ab60855..b6ea2c6 100644 --- a/m/pager.go +++ b/m/pager.go @@ -184,6 +184,7 @@ func NewPager(r *Reader) *Pager { ScrollLeftHint: twin.NewCell('<', twin.StyleDefault.WithAttr(twin.AttrReverse)), ScrollRightHint: twin.NewCell('>', twin.StyleDefault.WithAttr(twin.AttrReverse)), scrollPosition: newScrollPosition(name), + marks: make(map[rune]scrollPosition), } pager.mode = PagerModeViewing{pager: &pager} @@ -316,6 +317,7 @@ func (p *Pager) StartPaging(screen twin.Screen, chromaStyle *chroma.Style, chrom p.screen = screen p.linePrefix = getLineColorPrefix(chromaStyle, chromaFormatter) p.mode = PagerModeViewing{pager: p} + p.marks = make(map[rune]scrollPosition) go func() { for range p.reader.moreLinesAdded { diff --git a/m/pagermode-jump-to-mark.go b/m/pagermode-jump-to-mark.go index c7867f8..dd5132f 100644 --- a/m/pagermode-jump-to-mark.go +++ b/m/pagermode-jump-to-mark.go @@ -21,20 +21,17 @@ func (m PagerModeJumpToMark) drawFooter(_ string, _ string) { p.screen.SetCell(pos, height-1, twin.NewCell(token, twin.StyleDefault)) pos++ } - - // Add a cursor - p.screen.SetCell(pos, height-1, twin.NewCell(' ', twin.StyleDefault.WithAttr(twin.AttrReverse))) } func (m PagerModeJumpToMark) getMarkPrompt() string { // Special case having zero, one or multiple marks if len(m.pager.marks) == 0 { - return "Press \"m\" to set your first mark!" + return "No marks set, press 'm' to set one!" } if len(m.pager.marks) == 1 { for key := range m.pager.marks { - return "Press \"" + string(key) + "\" to jump to your mark!" + return "Jump to your mark: " + string(key) } } @@ -44,7 +41,7 @@ func (m PagerModeJumpToMark) getMarkPrompt() string { return marks[i] < marks[j] }) - prompt := "Press a key to jump to your mark: " + prompt := "Jump to one of these marks: " for i, mark := range marks { if i > 0 { prompt += ", " @@ -71,6 +68,12 @@ func (m PagerModeJumpToMark) onKey(key twin.KeyCode) { } func (m PagerModeJumpToMark) onRune(char rune) { + if len(m.pager.marks) == 0 && char == 'm' { + //nolint:gosimple // The linter's advice is just wrong here + m.pager.mode = PagerModeMark{pager: m.pager} + return + } + destination, ok := m.pager.marks[char] if ok { m.pager.scrollPosition = destination From 0f60fb4f459998c6928c2bd9ef0abb4ffe8a03b3 Mon Sep 17 00:00:00 2001 From: Johan Walles Date: Tue, 9 Jan 2024 15:39:59 +0100 Subject: [PATCH 5/6] Fix the help text --- m/pager.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/m/pager.go b/m/pager.go index b6ea2c6..cced57d 100644 --- a/m/pager.go +++ b/m/pager.go @@ -129,8 +129,8 @@ Moving around * Left / right can be used to hide / show line numbers * Home and End for start / end of the document * 'g' for going to a specific line number -* ' (single quote) sets a mark -* 'm' jumps to the mark +* 'm' sets a mark, you will be asked for a letter to label it with +* ' (single quote) jumps to the mark * CTRL-p moves to the previous line * CTRL-n moves to the next line * PageUp / 'b' and PageDown / 'f' From b2019308e1554986da3eb23cb9f9cf322ee76631 Mon Sep 17 00:00:00 2001 From: Johan Walles Date: Tue, 9 Jan 2024 15:41:44 +0100 Subject: [PATCH 6/6] Not needed, remove it --- m/pager.go | 1 - 1 file changed, 1 deletion(-) diff --git a/m/pager.go b/m/pager.go index cced57d..a37c664 100644 --- a/m/pager.go +++ b/m/pager.go @@ -184,7 +184,6 @@ func NewPager(r *Reader) *Pager { ScrollLeftHint: twin.NewCell('<', twin.StyleDefault.WithAttr(twin.AttrReverse)), ScrollRightHint: twin.NewCell('>', twin.StyleDefault.WithAttr(twin.AttrReverse)), scrollPosition: newScrollPosition(name), - marks: make(map[rune]scrollPosition), } pager.mode = PagerModeViewing{pager: &pager}