From 9834393cdbf948b94c5caa7f0b83c5d5fa1b9293 Mon Sep 17 00:00:00 2001 From: Noboru Saito Date: Sun, 5 Jan 2025 21:01:02 +0900 Subject: [PATCH 1/3] Add support for vertical header Add support for vertical header. Add vertical header option. --- main.go | 6 +++++ oviewer/draw.go | 56 ++++++++++++++++++++++++++++++++--------- oviewer/oviewer.go | 7 ++++++ oviewer/prepare_draw.go | 10 +++++--- 4 files changed, 64 insertions(+), 15 deletions(-) diff --git a/main.go b/main.go index 44251d30..632d7f6c 100644 --- a/main.go +++ b/main.go @@ -438,6 +438,12 @@ func init() { return []string{"1"}, cobra.ShellCompDirectiveNoFileComp }) + rootCmd.PersistentFlags().IntP("vertical-header", "V", 0, "number of vertical header lines to be displayed constantly") + _ = viper.BindPFlag("general.VerticalHeader", rootCmd.PersistentFlags().Lookup("vertical-header")) + _ = rootCmd.RegisterFlagCompletionFunc("vertical-header", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + return []string{"1"}, cobra.ShellCompDirectiveNoFileComp + }) + rootCmd.PersistentFlags().IntP("skip-lines", "", 0, "skip the number of lines") _ = viper.BindPFlag("general.SkipLines", rootCmd.PersistentFlags().Lookup("skip-lines")) _ = rootCmd.RegisterFlagCompletionFunc("skip-lines", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { diff --git a/oviewer/draw.go b/oviewer/draw.go index e83a92b5..d7c34589 100644 --- a/oviewer/draw.go +++ b/oviewer/draw.go @@ -20,11 +20,12 @@ func (root *Root) draw(ctx context.Context) { root.prepareDraw(ctx) // Body. - lX := m.topLX + lX := root.scr.verticalHeader lN := m.topLN + root.scr.headerEnd - // If WrapMode is off, lX is always 0. - if !m.WrapMode { - lX = 0 + + // If WrapMode is enabled, the drawing position is adjusted. + if m.WrapMode { + lX += m.topLX } lX, lN = root.drawBody(lX, lN) m.bottomLX = lX @@ -54,6 +55,7 @@ func (root *Root) drawBody(lX int, lN int) (int, int) { markStyleWidth := min(root.scr.vWidth, root.Doc.general.MarkStyleWidth) wrapNum := m.numOfWrap(lX, lN) + log.Println("wrapNum:", lN, wrapNum, m.width) for y := m.headerHeight; y < root.scr.vHeight-statusLine; y++ { line, ok := root.scr.lines[lN] if !ok { @@ -77,6 +79,7 @@ func (root *Root) drawBody(lX int, lN int) (int, int) { wrapNum++ if nextLX == 0 { wrapNum = 0 + nextLX = root.scr.verticalHeader } lX = nextLX @@ -91,7 +94,7 @@ func (root *Root) drawHeader() { // wrapNum is the number of wrapped lines. wrapNum := 0 - lX := 0 + lX := root.scr.verticalHeader lN := root.scr.headerLN for y := 0; y < m.headerHeight && lN < root.scr.headerEnd; y++ { lineC, ok := root.scr.lines[lN] @@ -118,7 +121,7 @@ func (root *Root) drawSectionHeader() { m := root.Doc wrapNum := 0 - lX := 0 + lX := root.scr.verticalHeader lN := root.scr.sectionHeaderLN for y := m.headerHeight; y < m.headerHeight+m.sectionHeaderHeight && lN < root.scr.sectionHeaderEnd; y++ { lineC, ok := root.scr.lines[lN] @@ -142,6 +145,7 @@ func (root *Root) drawSectionHeader() { wrapNum++ if nextLX == 0 { wrapNum = 0 + nextLX = root.scr.verticalHeader } lX = nextLX @@ -151,11 +155,40 @@ func (root *Root) drawSectionHeader() { // drawWrapLine wraps and draws the contents and returns the next drawing position. func (root *Root) drawLine(y int, lX int, lN int, lineC LineC) (int, int) { + if root.scr.verticalHeader > 0 { + root.drawVerticalHeader(y, lX, lineC) + } if root.Doc.WrapMode { return root.drawWrapLine(y, lX, lN, lineC) } - return root.drawNoWrapLine(y, root.Doc.x, lN, lineC) + return root.drawNoWrapLine(y, root.scr.verticalHeader+root.Doc.x, lN, lineC) +} + +// drawVerticalHeader draws the vertical header. +func (root *Root) drawVerticalHeader(y int, lX int, lineC LineC) { + numberWidth := root.scr.numberWidth + if numberWidth > 0 { + numberWidth += 1 + } + screen := root.Screen + for n := 0; n < root.scr.verticalHeader; n++ { + x := numberWidth + n + if lX > root.scr.verticalHeader || n >= len(lineC.lc) { + // EOL + style := lineC.eolStyle + if lineC.valid { + style = style.Reverse(true) + } + root.clearEOL(x, y, style) + break + } + c := lineC.lc[n] + if lineC.valid { + c.style = c.style.Reverse(true) + } + screen.SetContent(x, y, c.mainc, c.combc, c.style) + } } // drawWrapLine wraps and draws the contents and returns the next drawing position. @@ -171,7 +204,7 @@ func (root *Root) drawWrapLine(y int, lX int, lN int, lineC LineC) (int, int) { if lX+n >= len(lineC.lc) { // EOL root.clearEOL(x, y, lineC.eolStyle) - lX = 0 + lX = root.scr.verticalHeader lN++ break } @@ -208,8 +241,7 @@ func (root *Root) drawNoWrapLine(y int, lX int, lN int, lineC LineC) (int, int) screen.SetContent(x, y, c.mainc, c.combc, c.style) } lN++ - - return lX, lN + return 0, lN } // blankLineNumber should be blank for the line number. @@ -220,7 +252,7 @@ func (root *Root) blankLineNumber(y int) { if root.scr.startX <= 0 { return } - numC := StrToContents(strings.Repeat(" ", root.scr.startX-1), root.Doc.TabWidth) + numC := StrToContents(strings.Repeat(" ", root.scr.numberWidth), root.Doc.TabWidth) root.setContentString(0, y, numC) } @@ -248,7 +280,7 @@ func (root *Root) drawLineNumber(lN int, y int, valid bool) { number = number - m.firstLine() + 1 // Line numbers start at 1 except for skip and header lines. - numC := StrToContents(fmt.Sprintf("%*d", root.scr.startX-1, number), m.TabWidth) + numC := StrToContents(fmt.Sprintf("%*d", root.scr.numberWidth, number), m.TabWidth) for i := 0; i < len(numC); i++ { numC[i].style = applyStyle(defaultStyle, root.StyleLineNumber) } diff --git a/oviewer/oviewer.go b/oviewer/oviewer.go index 0af91b58..8fcb993d 100644 --- a/oviewer/oviewer.go +++ b/oviewer/oviewer.go @@ -100,6 +100,11 @@ type SCR struct { // sectionHeaderEnd is the end of the section header. sectionHeaderEnd int + // numWidth is the width of the number. + numberWidth int + // verticalHeader is the vertical header. + verticalHeader int + // x1, y1, x2, y2 are the coordinates selected by the mouse. x1 int y1 int @@ -147,6 +152,8 @@ type general struct { TabWidth int // Header is number of header lines to be fixed. Header int + // VerticalHeader is the number of vertical header lines. + VerticalHeader int // SkipLines is the rows to skip. SkipLines int // WatchInterval is the watch interval (seconds). diff --git a/oviewer/prepare_draw.go b/oviewer/prepare_draw.go index 5a145b87..59a4b900 100644 --- a/oviewer/prepare_draw.go +++ b/oviewer/prepare_draw.go @@ -37,7 +37,9 @@ func (root *Root) prepareScreen() { // prepareStartX prepares the start position of the x. func (root *Root) prepareStartX() { - root.scr.startX = 0 + root.scr.verticalHeader = root.General.VerticalHeader + root.scr.numberWidth = 0 + root.scr.startX = root.scr.verticalHeader m := root.Doc if !m.LineNumMode { return @@ -46,7 +48,8 @@ func (root *Root) prepareStartX() { if m.parent != nil { m = m.parent } - root.scr.startX = len(strconv.Itoa(m.BufEndNum())) + 1 + root.scr.numberWidth = len(strconv.Itoa(m.BufEndNum())) + root.scr.startX = root.scr.verticalHeader + root.scr.numberWidth + 1 } // ViewSync redraws the whole thing. @@ -73,7 +76,7 @@ func (root *Root) prepareDraw(ctx context.Context) { root.scr.headerEnd = root.Doc.firstLine() // Set the header height. root.Doc.headerHeight = root.Doc.getHeight(root.scr.headerLN, root.scr.headerEnd) - + log.Println("headerHeight:", root.Doc.headerHeight, root.Doc.width) // Section header. root.scr.sectionHeaderLN = -1 root.scr.sectionHeaderEnd = 0 @@ -306,6 +309,7 @@ func (m *Document) getHeight(startLN int, endLN int) int { for lN := startLN; lN < endLN; lN++ { height += len(m.leftMostX(lN)) } + log.Println("height:", startLN, endLN, height) return height } From 034c24ffdcbfa8e1b767affdb841ab140d3ec2d1 Mon Sep 17 00:00:00 2001 From: Noboru Saito Date: Tue, 7 Jan 2025 12:25:21 +0900 Subject: [PATCH 2/3] Fix the number of rows of wrap Fix the gap between the number of lines when wrapped. Deleted Debug line. Fixed the code and comments pointed out in the comments. --- oviewer/document.go | 3 +++ oviewer/draw.go | 3 +-- oviewer/move_vertical.go | 9 +++++++++ oviewer/oviewer.go | 2 +- oviewer/prepare_draw.go | 3 +-- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/oviewer/document.go b/oviewer/document.go index a67b83c5..0f9a866c 100644 --- a/oviewer/document.go +++ b/oviewer/document.go @@ -100,6 +100,9 @@ type Document struct { width int // height is the height of the screen. height int + // endExclude is the number of lines to exclude from the end. + endExclude int + // markedPoint is the position of the marked line. markedPoint int diff --git a/oviewer/draw.go b/oviewer/draw.go index d7c34589..b15b8a46 100644 --- a/oviewer/draw.go +++ b/oviewer/draw.go @@ -55,7 +55,6 @@ func (root *Root) drawBody(lX int, lN int) (int, int) { markStyleWidth := min(root.scr.vWidth, root.Doc.general.MarkStyleWidth) wrapNum := m.numOfWrap(lX, lN) - log.Println("wrapNum:", lN, wrapNum, m.width) for y := m.headerHeight; y < root.scr.vHeight-statusLine; y++ { line, ok := root.scr.lines[lN] if !ok { @@ -169,7 +168,7 @@ func (root *Root) drawLine(y int, lX int, lN int, lineC LineC) (int, int) { func (root *Root) drawVerticalHeader(y int, lX int, lineC LineC) { numberWidth := root.scr.numberWidth if numberWidth > 0 { - numberWidth += 1 + numberWidth++ } screen := root.Screen for n := 0; n < root.scr.verticalHeader; n++ { diff --git a/oviewer/move_vertical.go b/oviewer/move_vertical.go index 4308f89c..78b3e6f8 100644 --- a/oviewer/move_vertical.go +++ b/oviewer/move_vertical.go @@ -235,9 +235,18 @@ func (m *Document) leftMostX(lN int) []int { if err != nil { return nil } + lc = excludeRange(lc, m.endExclude) return leftX(m.width, lc) } +// excludeRange returns a new slice with the elements from start to end excluded. +func excludeRange(lc contents, end int) []content { + if end > len(lc) { + return []content{} + } + return lc[end:] +} + // leftX returns a list of left - most x positions when wrapping. func leftX(width int, lc contents) []int { if width <= 0 { diff --git a/oviewer/oviewer.go b/oviewer/oviewer.go index 8fcb993d..06006bb0 100644 --- a/oviewer/oviewer.go +++ b/oviewer/oviewer.go @@ -102,7 +102,7 @@ type SCR struct { // numWidth is the width of the number. numberWidth int - // verticalHeader is the vertical header. + // verticalHeader is the width of the vertical header. verticalHeader int // x1, y1, x2, y2 are the coordinates selected by the mouse. diff --git a/oviewer/prepare_draw.go b/oviewer/prepare_draw.go index 59a4b900..b0af9c43 100644 --- a/oviewer/prepare_draw.go +++ b/oviewer/prepare_draw.go @@ -41,6 +41,7 @@ func (root *Root) prepareStartX() { root.scr.numberWidth = 0 root.scr.startX = root.scr.verticalHeader m := root.Doc + m.endExclude = root.scr.verticalHeader if !m.LineNumMode { return } @@ -76,7 +77,6 @@ func (root *Root) prepareDraw(ctx context.Context) { root.scr.headerEnd = root.Doc.firstLine() // Set the header height. root.Doc.headerHeight = root.Doc.getHeight(root.scr.headerLN, root.scr.headerEnd) - log.Println("headerHeight:", root.Doc.headerHeight, root.Doc.width) // Section header. root.scr.sectionHeaderLN = -1 root.scr.sectionHeaderEnd = 0 @@ -309,7 +309,6 @@ func (m *Document) getHeight(startLN int, endLN int) int { for lN := startLN; lN < endLN; lN++ { height += len(m.leftMostX(lN)) } - log.Println("height:", startLN, endLN, height) return height } From 95fb196e1c635d96715a35c1c60d88feb1b6f313 Mon Sep 17 00:00:00 2001 From: Noboru Saito Date: Thu, 9 Jan 2025 22:57:08 +0900 Subject: [PATCH 3/3] Add vertical header style Added style to set with StyleVerticalHeader --- oviewer/config.go | 3 +++ oviewer/draw.go | 2 +- oviewer/oviewer.go | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/oviewer/config.go b/oviewer/config.go index fdcf6905..a356348a 100644 --- a/oviewer/config.go +++ b/oviewer/config.go @@ -32,6 +32,9 @@ func NewConfig() Config { StyleSectionLine: OVStyle{ Background: "slateblue", }, + StyleVerticalHeader: OVStyle{ + Reverse: true, + }, StyleMultiColorHighlight: []OVStyle{ {Foreground: "red"}, {Foreground: "aqua"}, diff --git a/oviewer/draw.go b/oviewer/draw.go index b15b8a46..bf6b3146 100644 --- a/oviewer/draw.go +++ b/oviewer/draw.go @@ -184,7 +184,7 @@ func (root *Root) drawVerticalHeader(y int, lX int, lineC LineC) { } c := lineC.lc[n] if lineC.valid { - c.style = c.style.Reverse(true) + c.style = applyStyle(defaultStyle, root.StyleVerticalHeader) } screen.SetContent(x, y, c.mainc, c.combc, c.style) } diff --git a/oviewer/oviewer.go b/oviewer/oviewer.go index 06006bb0..55ff51da 100644 --- a/oviewer/oviewer.go +++ b/oviewer/oviewer.go @@ -253,6 +253,9 @@ type Config struct { StyleOverStrike OVStyle // StyleOverLine is a style that applies to overstrike underlines. StyleOverLine OVStyle + // StyleVerticalHeader is a style that applies to the vertical header. + StyleVerticalHeader OVStyle + // General represents the general behavior. General general // BeforeWriteOriginal specifies the number of lines before the current position.