diff --git a/README.md b/README.md index e82949ee..a4fb62d2 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/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/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..8950371b 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: {"]"}, @@ -254,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") @@ -269,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") @@ -289,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") @@ -320,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") @@ -330,24 +326,22 @@ 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") 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") + 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") @@ -359,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) }