From a7967bc4e13883880559d3874187d5c123cdd64f Mon Sep 17 00:00:00 2001 From: Noboru Saito Date: Tue, 26 Dec 2023 10:29:04 +0900 Subject: [PATCH 1/2] Added input mode for section header number Added section-header-num to help-key and help options. README.md has also been updated, as it has been added to the help. Improved comments. --- README.md | 11 ++++++- oviewer/action.go | 18 +++++++++++ oviewer/draw.go | 2 +- oviewer/event.go | 2 ++ oviewer/input.go | 1 + oviewer/input_section_num.go | 61 ++++++++++++++++++++++++++++++++++++ oviewer/input_skip.go | 2 +- oviewer/input_viewmode.go | 2 +- oviewer/keybind.go | 4 +++ 9 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 oviewer/input_section_num.go diff --git a/README.md b/README.md index e82949ee..a662bb45 100644 --- a/README.md +++ b/README.md @@ -371,10 +371,14 @@ ov --section-delimiter "^#" --section-header README.md It is also useful as a pager for `git``. +The number of lines in section-header can be changed. + +You can specify the number of lines using the `--section-header-num` option or key input(default key `F7`). + ```gitconfig [pager] diff = "ov -F --section-delimiter '^diff' --section-header" - log = "ov -F --section-delimiter '^commit' --section-header" + log = "ov -F --section-delimiter '^commit' --section-header --section-header-num 3" ``` ### 3.10. Follow mode @@ -721,6 +725,7 @@ MemoryLimit: 1000 | -H, | --header int | number of header lines to be displayed constantly | | -h, | --help | help for ov | | | --help-key | display key bind information | +| | --hscroll-width [int\|int%\|.int] | width to scroll horizontally [int\|int%\|.int] (default "10%") | | | --incsearch[=true\|false] | incremental search (default true) | | -j, | --jump-target [int\|int%\|.int\|'section'] | jump target [int\|int%\|.int\|'section'] | | -n, | --line-number | line number mode | @@ -733,6 +738,7 @@ MemoryLimit: 1000 | | --regexp-search | regular expression search | | | --section-delimiter regexp | regexp for section delimiter .e.g. "^#" | | | --section-header | enable section-delimiter line as Header | +| | --section-header-num int | number of header lines (default 1) | | | --section-start int | section start position | | | --skip-extract | skip extracting compressed files | | | --skip-lines int | skip the number of lines | @@ -774,6 +780,8 @@ It can also be changed after startup. | [right] | * scroll to right | | [ctrl+left] | * scroll left half screen | | [ctrl+right] | * scroll right half screen | +| [ctrl+shift+left] | * scroll left specified width | +| [ctrl+shift+right] | * scroll right specified width | | [shift+Home] | * go to beginning of line | | [shift+End] | * go to end of line | | [g] | * go to line(input number or `.n` or `n%` allowed) | @@ -815,6 +823,7 @@ It can also be changed after startup. | [^] | * previous section | | [9] | * last section | | [F2] | * follow section mode toggle | +| [F7] | * section header number | | **Close and reload** | | | [ctrl+F9], [ctrl+alt+s] | * close file | | [ctrl+alt+l], [F5] | * reload file | diff --git a/oviewer/action.go b/oviewer/action.go index 43ba58ca..a8722062 100644 --- a/oviewer/action.go +++ b/oviewer/action.go @@ -321,6 +321,24 @@ func (root *Root) setSkipLines(input string) { root.setMessagef("Set skip lines %d", num) } +func (root *Root) setSectionNum(input string) { + num, err := strconv.Atoi(input) + if err != nil { + root.setMessagef("Set section header num: %s", ErrInvalidNumber.Error()) + return + } + if num < 0 { + root.setMessagef("Set section header num: %s", ErrOutOfRange.Error()) + return + } + if root.Doc.SectionHeaderNum == num { + return + } + + root.Doc.SectionHeaderNum = num + root.setMessagef("Set section header num %d", num) +} + // suspend suspends the current screen display and runs the shell. // It will return when you exit the shell. func (root *Root) suspend() { diff --git a/oviewer/draw.go b/oviewer/draw.go index 9989c43d..7157f188 100644 --- a/oviewer/draw.go +++ b/oviewer/draw.go @@ -120,7 +120,7 @@ func (root *Root) drawSectionHeader(lN int) int { } pn := lN - // If the line number is 0, it is the first line. + // prevSection searches for the section above the specified line. if pn == 0 { pn = 1 } diff --git a/oviewer/event.go b/oviewer/event.go index c7669a76..02e35e47 100644 --- a/oviewer/event.go +++ b/oviewer/event.go @@ -84,6 +84,8 @@ func (root *Root) eventLoop(ctx context.Context, quitChan chan<- struct{}) { root.setJumpTarget(ev.value) case *eventSaveBuffer: root.saveBuffer(ev.value) + case *eventSectionNum: + root.setSectionNum(ev.value) // tcell events case *tcell.EventResize: diff --git a/oviewer/input.go b/oviewer/input.go index 30c79354..6d0fb8be 100644 --- a/oviewer/input.go +++ b/oviewer/input.go @@ -29,6 +29,7 @@ const ( MultiColor // MultiColor is multi-word coloring. JumpTarget // JumpTarget is the position to display the search results. SaveBuffer // SaveBuffer is the save buffer. + SectionNum // SectionNum is the section number. ) // Input represents the status of various inputs. diff --git a/oviewer/input_section_num.go b/oviewer/input_section_num.go new file mode 100644 index 00000000..89c50c13 --- /dev/null +++ b/oviewer/input_section_num.go @@ -0,0 +1,61 @@ +package oviewer + +import ( + "strconv" + + "github.com/gdamore/tcell/v2" +) + +// setSectionNumMode sets the inputMode to SectionNum. +func (root *Root) setSectionNumMode() { + input := root.input + input.value = "" + input.cursorX = 0 + input.Event = newSectionNumEvent() +} + +// eventSectionNum represents the section num input mode. +type eventSectionNum struct { + tcell.EventTime + value string +} + +// newSectionNumEvent returns Event. +func newSectionNumEvent() *eventSectionNum { + return &eventSectionNum{} +} + +// Mode returns InputMode. +func (e *eventSectionNum) Mode() InputMode { + return SectionNum +} + +// Prompt returns the prompt string in the input field. +func (e *eventSectionNum) Prompt() string { + return "Section Num:" +} + +// Confirm returns the event when the input is confirmed. +func (e *eventSectionNum) Confirm(str string) tcell.Event { + e.value = str + e.SetEventNow() + return e +} + +// Up returns strings when the up key is pressed during input. +func (e *eventSectionNum) Up(str string) string { + n, err := strconv.Atoi(str) + if err != nil { + return "0" + } + return strconv.Itoa(n + 1) +} + +// Down returns strings when the down key is pressed during input. +func (e *eventSectionNum) Down(str string) string { + n, err := strconv.Atoi(str) + if err != nil || n <= 0 { + return "0" + } + return strconv.Itoa(n - 1) +} diff --git a/oviewer/input_skip.go b/oviewer/input_skip.go index 7859470a..9ff2d7c3 100644 --- a/oviewer/input_skip.go +++ b/oviewer/input_skip.go @@ -14,7 +14,7 @@ func (root *Root) setSkipLinesMode() { input.Event = newSkipLinesEvent() } -// eventSkipLines represents the goto input mode. +// eventSkipLines represents the skip lines input mode. type eventSkipLines struct { tcell.EventTime value string diff --git a/oviewer/input_viewmode.go b/oviewer/input_viewmode.go index ea92f0d9..f7bde499 100644 --- a/oviewer/input_viewmode.go +++ b/oviewer/input_viewmode.go @@ -19,7 +19,7 @@ func viewModeCandidate() *candidate { } } -// eventViewMode represents the mode input mode. +// eventViewMode represents the view mode input mode. type eventViewMode struct { tcell.EventTime clist *candidate diff --git a/oviewer/keybind.go b/oviewer/keybind.go index d1c99fb7..09d49d97 100644 --- a/oviewer/keybind.go +++ b/oviewer/keybind.go @@ -68,6 +68,7 @@ const ( actionSkipLines = "skip_lines" actionTabWidth = "tabwidth" actionGoLine = "goto" + actionSectionNum = "section_num" actionNextSearch = "next_search" actionNextBackSearch = "next_backsearch" actionNextDoc = "next_doc" @@ -147,6 +148,7 @@ func (root *Root) handlers() map[string]func() { actionSkipLines: root.setSkipLinesMode, actionTabWidth: root.setTabWidthMode, actionGoLine: root.setGoLineMode, + actionSectionNum: root.setSectionNumMode, actionNextSearch: root.sendNextSearch, actionNextBackSearch: root.sendNextBackSearch, actionNextDoc: root.nextDoc, @@ -229,6 +231,7 @@ func defaultKeyBinds() KeyBind { actionSkipLines: {"ctrl+s"}, actionTabWidth: {"t"}, actionGoLine: {"g"}, + actionSectionNum: {"F7"}, actionNextSearch: {"n"}, actionNextBackSearch: {"N"}, actionNextDoc: {"]"}, @@ -338,6 +341,7 @@ func (k KeyBind) String() string { k.writeKeyBind(&b, actionPrevSection, "previous section") k.writeKeyBind(&b, actionLastSection, "last section") k.writeKeyBind(&b, actionFollowSection, "follow section mode toggle") + k.writeKeyBind(&b, actionSectionNum, "section header number") fmt.Fprint(&b, "\n\tClose and reload\n") fmt.Fprint(&b, "\n") From fdb88e4e13463b1ac9bdd101f98593e138ae62f2 Mon Sep 17 00:00:00 2001 From: Noboru Saito Date: Tue, 26 Dec 2023 13:31:29 +0900 Subject: [PATCH 2/2] Turn repetition into a function Fixed missing section-header-num. --- README.md | 2 +- ov.yaml | 2 ++ oviewer/keybind.go | 34 ++++++++++++++-------------------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index a662bb45..a4fb62d2 100644 --- a/README.md +++ b/README.md @@ -378,7 +378,7 @@ You can specify the number of lines using the `--section-header-num` option or k ```gitconfig [pager] diff = "ov -F --section-delimiter '^diff' --section-header" - log = "ov -F --section-delimiter '^commit' --section-header --section-header-num 3" + log = "ov -F --section-delimiter '^commit' --section-header --section-header-num 3" ``` ### 3.10. Follow mode diff --git a/ov.yaml b/ov.yaml index 39fa270a..b9f813b2 100644 --- a/ov.yaml +++ b/ov.yaml @@ -134,6 +134,8 @@ KeyBind: section_start: - "ctrl+F3" - "alt+s" + section_header_num: + - "F7" next_section: - "space" last_section: diff --git a/oviewer/keybind.go b/oviewer/keybind.go index 09d49d97..8950371b 100644 --- a/oviewer/keybind.go +++ b/oviewer/keybind.go @@ -257,8 +257,7 @@ func defaultKeyBinds() KeyBind { // String returns keybind as a string for help. func (k KeyBind) String() string { var b strings.Builder - fmt.Fprint(&b, "\n\tKey binding\n") - fmt.Fprint(&b, "\n") + writeHeader(&b, "Key binding") k.writeKeyBind(&b, actionExit, "quit") k.writeKeyBind(&b, actionCancel, "cancel") k.writeKeyBind(&b, actionWriteExit, "output screen and quit") @@ -272,8 +271,7 @@ func (k KeyBind) String() string { k.writeKeyBind(&b, actionToggleMouse, "enable/disable mouse") k.writeKeyBind(&b, actionSaveBuffer, "save buffer to file") - fmt.Fprint(&b, "\n\tMoving\n") - fmt.Fprint(&b, "\n") + writeHeader(&b, "Moving") k.writeKeyBind(&b, actionMoveDown, "forward by one line") k.writeKeyBind(&b, actionMoveUp, "backward by one line") k.writeKeyBind(&b, actionMoveTop, "go to top of document") @@ -292,29 +290,25 @@ func (k KeyBind) String() string { k.writeKeyBind(&b, actionMoveEndRight, "go to end of line") k.writeKeyBind(&b, actionGoLine, "go to line(input number or `.n` or `n%` allowed)") - fmt.Fprint(&b, "\n\tMove document\n") - fmt.Fprint(&b, "\n") + writeHeader(&b, "Move document") k.writeKeyBind(&b, actionNextDoc, "next document") k.writeKeyBind(&b, actionPreviousDoc, "previous document") k.writeKeyBind(&b, actionCloseDoc, "close current document") - fmt.Fprint(&b, "\n\tMark position\n") - fmt.Fprint(&b, "\n") + writeHeader(&b, "Mark position") k.writeKeyBind(&b, actionMark, "mark current position") k.writeKeyBind(&b, actionRemoveMark, "remove mark current position") k.writeKeyBind(&b, actionRemoveAllMark, "remove all mark") k.writeKeyBind(&b, actionMoveMark, "move to next marked position") k.writeKeyBind(&b, actionMovePrevMark, "move to previous marked position") - fmt.Fprint(&b, "\n\tSearch\n") - fmt.Fprint(&b, "\n") + writeHeader(&b, "Search") k.writeKeyBind(&b, actionSearch, "forward search mode") k.writeKeyBind(&b, actionBackSearch, "backward search mode") k.writeKeyBind(&b, actionNextSearch, "repeat forward search") k.writeKeyBind(&b, actionNextBackSearch, "repeat backward search") - fmt.Fprint(&b, "\n\tChange display\n") - fmt.Fprint(&b, "\n") + writeHeader(&b, "Change display") k.writeKeyBind(&b, actionWrap, "wrap/nowrap toggle") k.writeKeyBind(&b, actionColumnMode, "column mode toggle") k.writeKeyBind(&b, actionColumnWidth, "column width toggle") @@ -323,8 +317,7 @@ func (k KeyBind) String() string { k.writeKeyBind(&b, actionLineNumMode, "line number toggle") k.writeKeyBind(&b, actionPlain, "original decoration toggle(plain)") - fmt.Fprint(&b, "\n\tChange Display with Input\n") - fmt.Fprint(&b, "\n") + writeHeader(&b, "Change Display with Input") k.writeKeyBind(&b, actionViewMode, "view mode selection") k.writeKeyBind(&b, actionDelimiter, "column delimiter string") k.writeKeyBind(&b, actionHeader, "number of header lines") @@ -333,8 +326,7 @@ func (k KeyBind) String() string { k.writeKeyBind(&b, actionMultiColor, "multi color highlight") k.writeKeyBind(&b, actionJumpTarget, "jump target(`.n` or `n%` or `section` allowed)") - fmt.Fprint(&b, "\n\tSection\n") - fmt.Fprint(&b, "\n") + writeHeader(&b, "Section") k.writeKeyBind(&b, actionSection, "section delimiter regular expression") k.writeKeyBind(&b, actionSectionStart, "section start position") k.writeKeyBind(&b, actionNextSection, "next section") @@ -343,15 +335,13 @@ func (k KeyBind) String() string { k.writeKeyBind(&b, actionFollowSection, "follow section mode toggle") k.writeKeyBind(&b, actionSectionNum, "section header number") - fmt.Fprint(&b, "\n\tClose and reload\n") - fmt.Fprint(&b, "\n") + writeHeader(&b, "Close and reload") k.writeKeyBind(&b, actionCloseFile, "close file") k.writeKeyBind(&b, actionReload, "reload file") k.writeKeyBind(&b, actionWatch, "watch mode") k.writeKeyBind(&b, actionWatchInterval, "set watch interval") - fmt.Fprint(&b, "\n\tKey binding when typing\n") - fmt.Fprint(&b, "\n") + writeHeader(&b, "Key binding when typing") k.writeKeyBind(&b, inputCaseSensitive, "case-sensitive toggle") k.writeKeyBind(&b, inputSmartCaseSensitive, "smart case-sensitive toggle") k.writeKeyBind(&b, inputRegexpSearch, "regular expression search toggle") @@ -363,6 +353,10 @@ func (k KeyBind) String() string { return b.String() } +func writeHeader(w io.Writer, header string) { + fmt.Fprintf(w, "\n\t%s\n", header) +} + func (k KeyBind) writeKeyBind(w io.Writer, action string, detail string) { fmt.Fprintf(w, " %-28s * %s\n", "["+strings.Join(k[action], "], [")+"]", detail) }