Skip to content

Commit

Permalink
Merge pull request #611 from noborus/refactor-align
Browse files Browse the repository at this point in the history
Add a refactorig and test of Align Converter
  • Loading branch information
noborus authored Aug 25, 2024
2 parents de00f0e + 5e71552 commit c3075e6
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 55 deletions.
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ func init() {
rootCmd.PersistentFlags().StringP("converter", "", "es", "converter [es|raw]")
_ = viper.BindPFlag("general.Converter", rootCmd.PersistentFlags().Lookup("converter"))
_ = rootCmd.RegisterFlagCompletionFunc("converter", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"es\tEscape Sequence", "raw\tRaw"}, cobra.ShellCompDirectiveNoFileComp
return []string{"es\tEscape Sequence", "raw\tRaw output of escape sequences", "align\tAlign Column Widths"}, cobra.ShellCompDirectiveNoFileComp
})

rootCmd.PersistentFlags().IntP("tab-width", "x", 8, "tab stop width")
Expand Down
7 changes: 5 additions & 2 deletions oviewer/convert_align.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ func newAlignConverter(widthF bool) *align {
// Returns true if it is an escape sequence and a non-printing character.
func (a *align) convert(st *parseState) bool {
if len(st.lc) == 0 {
if a.delimiter == "\t" {
st.tabWidth = 1
}
a.reset()
}
if a.es.convert(st) {
Expand Down Expand Up @@ -73,7 +76,7 @@ func (a *align) convertDelm(st *parseState) bool {
lc = append(lc, st.lc[s:e]...)
width := e - s
// Add space to align columns.
for ; width < a.maxWidths[c]; width++ {
for ; width < a.maxWidths[c]+1; width++ {
lc = append(lc, SpaceContent)
}
s = e
Expand All @@ -97,7 +100,7 @@ func (a *align) convertWidth(st *parseState) bool {
}
lc = append(lc, st.lc[s:e]...)
// Add space to align columns.
for ; width <= a.maxWidths[i]; width++ {
for ; width <= a.maxWidths[i]+1; width++ {
lc = append(lc, SpaceContent)
}
s = e
Expand Down
8 changes: 4 additions & 4 deletions oviewer/convert_raw.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package oviewer

type raw struct{}
type rawConverter struct{}

func newRawConverter() *raw {
return &raw{}
func newRawConverter() *rawConverter {
return &rawConverter{}
}

// convert converts only escape sequence codes to display characters and returns them as is.
// Returns true if it is an escape sequence and a non-printing character.
func (raw) convert(st *parseState) bool {
func (rawConverter) convert(st *parseState) bool {
if st.mainc != 0x1b {
return false
}
Expand Down
10 changes: 5 additions & 5 deletions oviewer/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ func NewDocument() (*Document, error) {
ColumnDelimiter: "",
TabWidth: 8,
MarkStyleWidth: 1,
Converter: "es",
Converter: esConv,
},
ctlCh: make(chan controlSpecifier),
memoryLimit: 100,
Expand Down Expand Up @@ -243,11 +243,11 @@ func (m *Document) NewCache() error {
// converterType returns the Converter type.
func (m *Document) converterType(name string) Converter {
switch name {
case "raw":
case rawConv:
return newRawConverter()
case "es":
case esConv:
return newESConverter()
case "align":
case alignConv:
return m.alignConv
}
return defaultConverter
Expand Down Expand Up @@ -318,7 +318,7 @@ func (m *Document) Line(n int) ([]byte, error) {
}

s := m.store
if n >= m.BufEndNum() {
if n < 0 || n >= m.BufEndNum() {
return nil, fmt.Errorf("%w %d>%d", ErrOutOfRange, n, m.BufEndNum())
}

Expand Down
5 changes: 3 additions & 2 deletions oviewer/input_converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ func (root *Root) setConvertType(context.Context) {
func converterCandidate() *candidate {
return &candidate{
list: []string{
"es",
"raw",
esConv,
rawConv,
alignConv,
},
}
}
Expand Down
6 changes: 6 additions & 0 deletions oviewer/oviewer.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,12 @@ const QuitSmallCountDown = 10
// when the debug flag is enabled.
const MaxWriteLog int = 10

const (
esConv = "es"
rawConv = "raw"
alignConv = "align"
)

var (
// ErrOutOfRange indicates that value is out of range.
ErrOutOfRange = errors.New("out of range")
Expand Down
92 changes: 51 additions & 41 deletions oviewer/prepare_draw.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log"
"math"
"reflect"
"regexp"
"sort"
"strconv"
"time"
Expand Down Expand Up @@ -87,96 +88,101 @@ func (root *Root) prepareDraw(ctx context.Context) {
root.Doc.setColumnWidths()
}

// Sets the maximum width of a column.
if root.Doc.Converter == "align" {
root.setAlignColumnWidths()
// Sets alignConv if the converter is align.
if root.Doc.Converter == alignConv {
root.setAlignConverter()
}

// Prepare the lines.
root.scr.lines = root.prepareLines(root.scr.lines)
}

// setAlignColumnWidths sets the maximum width of the column.
func (root *Root) setAlignColumnWidths() {
// setAlignConverter sets the maximum width of the column.
func (root *Root) setAlignConverter() {
m := root.Doc

m.alignConv.WidthF = m.ColumnWidth
if !m.alignConv.WidthF {
if !m.ColumnWidth {
root.Doc.alignConv.delimiter = m.ColumnDelimiter
root.Doc.alignConv.delimiterReg = m.ColumnDelimiterReg
}

maxWidths := make([]int, 0, len(m.alignConv.maxWidths))
for ln := root.scr.headerLN; ln < root.scr.headerEnd; ln++ {
maxWidths = m.maxWidths(maxWidths, ln)
maxWidths = m.maxColumnWidths(maxWidths, ln)
}
for ln := root.scr.sectionHeaderLN; ln < root.scr.sectionHeaderEnd; ln++ {
maxWidths = m.maxWidths(maxWidths, ln)
maxWidths = m.maxColumnWidths(maxWidths, ln)
}
startLN := m.topLN + m.firstLine()
endLN := startLN + root.scr.vHeight
for ln := startLN; ln < endLN; ln++ {
maxWidths = m.maxWidths(maxWidths, ln)
for ln := startLN; ln < startLN+root.scr.vHeight; ln++ {
maxWidths = m.maxColumnWidths(maxWidths, ln)
}

if !reflect.DeepEqual(m.alignConv.maxWidths, maxWidths) {
m.alignConv.orgWidths = m.columnWidths
m.alignConv.maxWidths = maxWidths
m.ClearCache()
log.Println("Change in column width")
} else {
log.Println("No change in column width")
}
}

// maxWidths returns the maximum width of the column.
func (m *Document) maxWidths(columnWidth []int, lN int) []int {
// maxColumnWidths returns the maximum width of the column.
func (m *Document) maxColumnWidths(maxWidths []int, lN int) []int {
if lN < 0 {
return maxWidths
}
str, err := m.LineStr(lN)
if err != nil {
return columnWidth
return maxWidths
}
lc := StrToContents(str, m.TabWidth)
if m.ColumnWidth {
return m.maxWidthsWidth(lc, columnWidth)
return maxWidthsWidth(lc, maxWidths, m.columnWidths)
}
return m.maxWidthsDelm(lc, columnWidth)
return maxWidthsDelm(lc, maxWidths, m.ColumnDelimiter, m.ColumnDelimiterReg)
}

// maxWidthsDelm returns the maximum width of the column.
func (m *Document) maxWidthsDelm(lc contents, columnWidth []int) []int {
func maxWidthsDelm(lc contents, maxWidths []int, delimiter string, delimiterReg *regexp.Regexp) []int {
str, pos := ContentsToStr(lc)
indexes := allIndex(str, m.ColumnDelimiter, m.ColumnDelimiterReg)
indexes := allIndex(str, delimiter, delimiterReg)
if len(indexes) == 0 {
return columnWidth
return maxWidths
}
s := 0
for i := 0; i < len(indexes); i++ {
e := pos.x(indexes[i][1])
width := e - s
if len(columnWidth) <= i {
columnWidth = append(columnWidth, width)
if len(maxWidths) <= i {
maxWidths = append(maxWidths, width)
} else {
columnWidth[i] = max(width, columnWidth[i])
maxWidths[i] = max(width, maxWidths[i])
}
s = e
s = e + 1
}
return columnWidth
return maxWidths
}

// maxWidthsWidth returns the maximum width of the column.
func (m *Document) maxWidthsWidth(lc contents, columnWidth []int) []int {
indexes := m.columnWidths
if len(indexes) == 0 {
return columnWidth
func maxWidthsWidth(lc contents, maxWidths []int, widths []int) []int {
if len(widths) == 0 {
return maxWidths
}
s := 0
for i := 0; i < len(indexes); i++ {
e := findColumnEnd(lc, indexes, i) + 1
for i := 0; i < len(widths); i++ {
e := findColumnEnd(lc, widths, i) + 1
width := e - s
if len(columnWidth) <= i {
columnWidth = append(columnWidth, width)
if len(maxWidths) <= i {
maxWidths = append(maxWidths, width)
} else {
columnWidth[i] = max(width, columnWidth[i])
maxWidths[i] = max(width, maxWidths[i])
}
s = e
}
return columnWidth
return maxWidths
}

// shiftBody shifts the section header so that it is not hidden by it.
Expand Down Expand Up @@ -457,13 +463,8 @@ func (root *Root) columnWidthHighlight(line LineC) {
start, end := -1, -1
for c := 0; c < len(indexes)+1; c++ {
start = end + 1
if m.Converter == "align" {
l := len(line.lc)
if c < len(m.alignConv.maxWidths) {
l = m.alignConv.maxWidths[c]
}
end = start + l
end = min(end, len(line.lc))
if m.Converter == alignConv {
end = alignColumnEnd(line.lc, m.alignConv.maxWidths, c, start)
} else {
end = findColumnEnd(line.lc, indexes, c)
}
Expand Down Expand Up @@ -515,6 +516,15 @@ func findColumnEnd(lc contents, indexes []int, n int) int {
return f
}

// alignColumnEnd returns the position of the end of a column.
func alignColumnEnd(lc contents, widths []int, n int, start int) int {
if len(widths) <= n {
return len(lc)
}
end := start + widths[n] + 1
return min(end, len(lc))
}

// RangeStyle applies the style to the specified range.
// Apply style to contents.
func RangeStyle(lc contents, start int, end int, s OVStyle) {
Expand Down
75 changes: 75 additions & 0 deletions oviewer/prepare_draw_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"path/filepath"
"reflect"
"regexp"
"testing"
"time"

Expand Down Expand Up @@ -1069,3 +1070,77 @@ func TestRoot_sectionHeader(t *testing.T) {
})
}
}

func Test_maxWidthsDelm(t *testing.T) {
type args struct {
maxWidths []int
delimiter string
delimiterReg *regexp.Regexp
}
tests := []struct {
name string
str string
args args
want []int
}{
{
name: "Test maxWidthsDelm1",
str: "a, b, c, d, e, f, g",
args: args{
maxWidths: []int{0, 0, 0, 0, 0},
delimiter: ",",
delimiterReg: regexpCompile(",", false),
},
want: []int{2, 2, 2, 2, 2, 2},
},
{
name: "Test maxWidthsDelm2",
str: "no delimiter",
args: args{
maxWidths: []int{2, 2, 2, 2, 2, 2},
delimiter: ",",
delimiterReg: regexpCompile(",", false),
},
want: []int{2, 2, 2, 2, 2, 2},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
lc := StrToContents(tt.str, 8)
if got := maxWidthsDelm(lc, tt.args.maxWidths, tt.args.delimiter, tt.args.delimiterReg); !reflect.DeepEqual(got, tt.want) {
t.Errorf("maxWidthsDelm() = %v, want %v", got, tt.want)
}
})
}
}

func Test_maxWidthsWidth(t *testing.T) {
type args struct {
maxWidths []int
widths []int
}
tests := []struct {
name string
str string
args args
want []int
}{
{
name: "Test maxWidthsWidth1",
str: " 1 tsst 21",
args: args{
maxWidths: []int{0},
widths: []int{2, 11},
},
want: []int{3, 9},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
lc := StrToContents(tt.str, 8)
if got := maxWidthsWidth(lc, tt.args.maxWidths, tt.args.widths); !reflect.DeepEqual(got, tt.want) {
t.Errorf("maxWidthsWidth() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit c3075e6

Please sign in to comment.