From 1aba280365cf81571d80827f392359c2010b1d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lovro=20Ma=C5=BEgon?= Date: Thu, 8 Dec 2022 14:49:38 +0100 Subject: [PATCH 01/25] add gauge threshold --- widgets/gauge/gauge.go | 31 +++++++-- widgets/gauge/gauge_test.go | 94 ++++++++++++++++++++++++++++ widgets/gauge/gaugedemo/gaugedemo.go | 2 + widgets/gauge/options.go | 18 ++++++ 4 files changed, 140 insertions(+), 5 deletions(-) diff --git a/widgets/gauge/gauge.go b/widgets/gauge/gauge.go index e850b59..f0f1668 100644 --- a/widgets/gauge/gauge.go +++ b/widgets/gauge/gauge.go @@ -136,10 +136,12 @@ func (g *Gauge) Percent(p int, opts ...Option) error { return nil } -// width determines the required width of the gauge drawn on the provided area -// in order to represent the current progress. -func (g *Gauge) width(ar image.Rectangle) int { - mult := float32(g.current) / float32(g.total) +// width determines the X coordinate that represents point w in rectangle ar. +// This is used to calculate the width of the gauge drawn on the provided area +// in order to represent the current progress or to figure out the coordinate +// for the threshold line. +func (g *Gauge) width(ar image.Rectangle, w int) int { + mult := float32(w) / float32(g.total) width := float32(ar.Dx()) * mult return int(width) } @@ -273,7 +275,7 @@ func (g *Gauge) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error { progress := image.Rect( usable.Min.X, usable.Min.Y, - usable.Min.X+g.width(usable), + usable.Min.X+g.width(usable, g.current), usable.Max.Y, ) if progress.Dx() > 0 { @@ -284,6 +286,25 @@ func (g *Gauge) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error { return err } } + if g.opts.threshold > 0 && g.opts.threshold < g.total { + line := draw.HVLine{ + Start: image.Point{ + X: usable.Min.X + g.width(usable, g.opts.threshold), + Y: cvs.Area().Min.Y, + }, + End: image.Point{ + X: usable.Min.X + g.width(usable, g.opts.threshold), + Y: cvs.Area().Max.Y - 1, + }, + } + if err := draw.HVLines(cvs, []draw.HVLine{line}, + draw.HVLineStyle(g.opts.thresholdLineStyle), + draw.HVLineCellOpts(g.opts.thresholdCellOpts...), + ); err != nil { + return err + } + } + return g.drawText(cvs, progress) } diff --git a/widgets/gauge/gauge_test.go b/widgets/gauge/gauge_test.go index cb43297..c182be7 100644 --- a/widgets/gauge/gauge_test.go +++ b/widgets/gauge/gauge_test.go @@ -841,6 +841,100 @@ func TestGauge(t *testing.T) { return ft }, }, + { + desc: "threshold with border percentage", + opts: []Option{ + Char('o'), + Threshold(20, linestyle.Double), + }, + percent: &percentCall{p: 35}, + canvas: image.Rect(0, 0, 10, 3), + want: func(size image.Point) *faketerm.Terminal { + ft := faketerm.MustNew(size) + c := testcanvas.MustNew(ft.Area()) + + testdraw.MustRectangle(c, image.Rect(0, 0, 3, 3), + draw.RectChar('o'), + draw.RectCellOpts(cell.BgColor(cell.ColorGreen)), + ) + testdraw.MustText(c, "35%", image.Point{3, 1}) + testdraw.MustHVLines(c, []draw.HVLine{{ + Start: image.Point{X: 2, Y: 0}, + End: image.Point{X: 2, Y: 2}, + }}, draw.HVLineStyle(linestyle.Double)) + testcanvas.MustApply(c, ft) + return ft + }, + }, + { + desc: "threshold without border absolute", + opts: []Option{ + Char('o'), + Threshold(3, linestyle.Light, cell.BgColor(cell.ColorRed)), + Border(linestyle.None), + HideTextProgress(), + }, + absolute: &absoluteCall{done: 4, total: 10}, + canvas: image.Rect(0, 0, 10, 3), + want: func(size image.Point) *faketerm.Terminal { + ft := faketerm.MustNew(size) + c := testcanvas.MustNew(ft.Area()) + + testdraw.MustRectangle(c, image.Rect(0, 0, 3, 3), + draw.RectChar('o'), + draw.RectCellOpts(cell.BgColor(cell.ColorGreen)), + ) + testdraw.MustHVLines(c, []draw.HVLine{{ + Start: image.Point{X: 3, Y: 0}, + End: image.Point{X: 3, Y: 2}, + }}, draw.HVLineStyle(linestyle.Light), + draw.HVLineCellOpts(cell.BgColor(cell.ColorRed))) + testcanvas.MustApply(c, ft) + return ft + }, + }, + { + desc: "threshold outside of bounds (negative)", + opts: []Option{ + Char('o'), + HideTextProgress(), + Threshold(-1, linestyle.Light), // ignored + }, + percent: &percentCall{p: 35}, + canvas: image.Rect(0, 0, 10, 3), + want: func(size image.Point) *faketerm.Terminal { + ft := faketerm.MustNew(size) + c := testcanvas.MustNew(ft.Area()) + + testdraw.MustRectangle(c, image.Rect(0, 0, 3, 3), + draw.RectChar('o'), + draw.RectCellOpts(cell.BgColor(cell.ColorGreen)), + ) + testcanvas.MustApply(c, ft) + return ft + }, + }, + { + desc: "threshold outside of bounds (>=max)", + opts: []Option{ + Char('o'), + HideTextProgress(), + Threshold(100, linestyle.Light), // ignored + }, + percent: &percentCall{p: 35}, + canvas: image.Rect(0, 0, 10, 3), + want: func(size image.Point) *faketerm.Terminal { + ft := faketerm.MustNew(size) + c := testcanvas.MustNew(ft.Area()) + + testdraw.MustRectangle(c, image.Rect(0, 0, 3, 3), + draw.RectChar('o'), + draw.RectCellOpts(cell.BgColor(cell.ColorGreen)), + ) + testcanvas.MustApply(c, ft) + return ft + }, + }, } for _, tc := range tests { diff --git a/widgets/gauge/gaugedemo/gaugedemo.go b/widgets/gauge/gaugedemo/gaugedemo.go index 96696e9..0fc6628 100644 --- a/widgets/gauge/gaugedemo/gaugedemo.go +++ b/widgets/gauge/gaugedemo/gaugedemo.go @@ -101,6 +101,7 @@ func main() { gauge.Color(cell.ColorNumber(33)), gauge.Border(linestyle.Light), gauge.BorderTitle("Absolute progress"), + gauge.Threshold(43, linestyle.Light, cell.FgColor(cell.ColorRed)), ) if err != nil { panic(err) @@ -124,6 +125,7 @@ func main() { gauge.Color(cell.ColorRed), gauge.FilledTextColor(cell.ColorBlack), gauge.EmptyTextColor(cell.ColorYellow), + gauge.Threshold(20, linestyle.Double), ) if err != nil { panic(err) diff --git a/widgets/gauge/options.go b/widgets/gauge/options.go index 0540abb..309f7dd 100644 --- a/widgets/gauge/options.go +++ b/widgets/gauge/options.go @@ -47,6 +47,10 @@ type options struct { borderCellOpts []cell.Option borderTitle string borderTitleHAlign align.Horizontal + // If set draws a vertical line representing the threshold. + threshold int + thresholdCellOpts []cell.Option + thresholdLineStyle linestyle.LineStyle } // newOptions returns options with the default values set. @@ -201,3 +205,17 @@ func BorderTitleAlign(h align.Horizontal) Option { opts.borderTitleHAlign = h }) } + +// Threshold configures the Gauge to display a vertical threshold line at value +// t. If the progress is set by a call to Percent(), t represents a percentage, +// e.g. "40" means line is displayed at 40%. If the progress is set by a call to +// Absolute(), the threshold is also considered an absolute number. +// Threshold needs to be configured inside the valid values of a gauge to be +// displayed, if it's set to <=0 or >=total it won't have any effect. +func Threshold(t int, ls linestyle.LineStyle, cOpts ...cell.Option) Option { + return option(func(opts *options) { + opts.threshold = t + opts.thresholdLineStyle = ls + opts.thresholdCellOpts = cOpts + }) +} From 6e46b739275ae0c6007d09981e77388ccdb369b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lovro=20Ma=C5=BEgon?= Date: Fri, 30 Dec 2022 13:01:07 +0100 Subject: [PATCH 02/25] refactor drawing of gauge threshold --- widgets/gauge/gauge.go | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/widgets/gauge/gauge.go b/widgets/gauge/gauge.go index f0f1668..b9410eb 100644 --- a/widgets/gauge/gauge.go +++ b/widgets/gauge/gauge.go @@ -159,6 +159,11 @@ func (g *Gauge) usable(cvs *canvas.Canvas) image.Rectangle { return cvs.Area() } +// thresholdVisible determines if the threshold line should be drawn. +func (g *Gauge) thresholdVisible() bool { + return g.opts.threshold > 0 && g.opts.threshold < g.total +} + // progressText returns the textual representation of the current progress. func (g *Gauge) progressText() string { if g.opts.hideTextProgress { @@ -246,6 +251,26 @@ func (g *Gauge) drawText(cvs *canvas.Canvas, progress image.Rectangle) error { return nil } +// drawThreshold draws the threshold line. +func (g *Gauge) drawThreshold(cvs *canvas.Canvas) error { + ar := g.usable(cvs) + + line := draw.HVLine{ + Start: image.Point{ + X: ar.Min.X + g.width(ar, g.opts.threshold), + Y: cvs.Area().Min.Y, + }, + End: image.Point{ + X: ar.Min.X + g.width(ar, g.opts.threshold), + Y: cvs.Area().Max.Y - 1, + }, + } + return draw.HVLines(cvs, []draw.HVLine{line}, + draw.HVLineStyle(g.opts.thresholdLineStyle), + draw.HVLineCellOpts(g.opts.thresholdCellOpts...), + ) +} + // Draw draws the Gauge widget onto the canvas. // Implements widgetapi.Widget.Draw. func (g *Gauge) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error { @@ -286,21 +311,8 @@ func (g *Gauge) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error { return err } } - if g.opts.threshold > 0 && g.opts.threshold < g.total { - line := draw.HVLine{ - Start: image.Point{ - X: usable.Min.X + g.width(usable, g.opts.threshold), - Y: cvs.Area().Min.Y, - }, - End: image.Point{ - X: usable.Min.X + g.width(usable, g.opts.threshold), - Y: cvs.Area().Max.Y - 1, - }, - } - if err := draw.HVLines(cvs, []draw.HVLine{line}, - draw.HVLineStyle(g.opts.thresholdLineStyle), - draw.HVLineCellOpts(g.opts.thresholdCellOpts...), - ); err != nil { + if g.thresholdVisible() { + if err := g.drawThreshold(cvs); err != nil { return err } } From 9beb36080d44aca801813ab47b19a40db9f8d48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lovro=20Ma=C5=BEgon?= Date: Fri, 30 Dec 2022 13:05:38 +0100 Subject: [PATCH 03/25] return error if gauge threshold is negative --- widgets/gauge/gauge_test.go | 25 ++----------------------- widgets/gauge/options.go | 9 ++++++--- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/widgets/gauge/gauge_test.go b/widgets/gauge/gauge_test.go index c182be7..17c629d 100644 --- a/widgets/gauge/gauge_test.go +++ b/widgets/gauge/gauge_test.go @@ -70,9 +70,9 @@ func TestGauge(t *testing.T) { wantErr: true, }, { - desc: "fails on negative height", + desc: "fails on negative threshold", opts: []Option{ - Height(-1), + Threshold(-1, linestyle.Light), }, canvas: image.Rect(0, 0, 10, 3), want: func(size image.Point) *faketerm.Terminal { @@ -893,27 +893,6 @@ func TestGauge(t *testing.T) { return ft }, }, - { - desc: "threshold outside of bounds (negative)", - opts: []Option{ - Char('o'), - HideTextProgress(), - Threshold(-1, linestyle.Light), // ignored - }, - percent: &percentCall{p: 35}, - canvas: image.Rect(0, 0, 10, 3), - want: func(size image.Point) *faketerm.Terminal { - ft := faketerm.MustNew(size) - c := testcanvas.MustNew(ft.Area()) - - testdraw.MustRectangle(c, image.Rect(0, 0, 3, 3), - draw.RectChar('o'), - draw.RectCellOpts(cell.BgColor(cell.ColorGreen)), - ) - testcanvas.MustApply(c, ft) - return ft - }, - }, { desc: "threshold outside of bounds (>=max)", opts: []Option{ diff --git a/widgets/gauge/options.go b/widgets/gauge/options.go index 309f7dd..ddb2427 100644 --- a/widgets/gauge/options.go +++ b/widgets/gauge/options.go @@ -70,6 +70,9 @@ func (o *options) validate() error { if got, min := o.height, 0; got < min { return fmt.Errorf("invalid Height %d, must be %d <= Height", got, min) } + if got, min := o.threshold, 0; got < min { + return fmt.Errorf("invalid Threshold %d, must be %d <= Threshold", got, min) + } return nil } @@ -209,9 +212,9 @@ func BorderTitleAlign(h align.Horizontal) Option { // Threshold configures the Gauge to display a vertical threshold line at value // t. If the progress is set by a call to Percent(), t represents a percentage, // e.g. "40" means line is displayed at 40%. If the progress is set by a call to -// Absolute(), the threshold is also considered an absolute number. -// Threshold needs to be configured inside the valid values of a gauge to be -// displayed, if it's set to <=0 or >=total it won't have any effect. +// Absolute(), the threshold is considered an absolute number. +// Threshold must be positive to be displayed. If the threshold is zero or +// greater than total, it won't be displayed. Defaults to zero. func Threshold(t int, ls linestyle.LineStyle, cOpts ...cell.Option) Option { return option(func(opts *options) { opts.threshold = t From bc37cb70a74c634db21ba03702bb265600799b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lovro=20Ma=C5=BEgon?= Date: Fri, 30 Dec 2022 13:18:28 +0100 Subject: [PATCH 04/25] more gauge threshold tests --- widgets/gauge/gauge_test.go | 78 +++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/widgets/gauge/gauge_test.go b/widgets/gauge/gauge_test.go index 17c629d..13e35e9 100644 --- a/widgets/gauge/gauge_test.go +++ b/widgets/gauge/gauge_test.go @@ -914,6 +914,84 @@ func TestGauge(t *testing.T) { return ft }, }, + { + desc: "progress below threshold", + opts: []Option{ + Char('o'), + Threshold(5, linestyle.Light, cell.BgColor(cell.ColorRed)), + HideTextProgress(), + }, + absolute: &absoluteCall{done: 4, total: 10}, + canvas: image.Rect(0, 0, 10, 3), + want: func(size image.Point) *faketerm.Terminal { + ft := faketerm.MustNew(size) + c := testcanvas.MustNew(ft.Area()) + + testdraw.MustRectangle(c, image.Rect(0, 0, 4, 3), + draw.RectChar('o'), + draw.RectCellOpts(cell.BgColor(cell.ColorGreen)), + ) + testdraw.MustHVLines(c, []draw.HVLine{{ + Start: image.Point{X: 5, Y: 0}, + End: image.Point{X: 5, Y: 2}, + }}, draw.HVLineStyle(linestyle.Light), + draw.HVLineCellOpts(cell.BgColor(cell.ColorRed))) + testcanvas.MustApply(c, ft) + return ft + }, + }, + { + desc: "progress exactly at threshold", + opts: []Option{ + Char('o'), + Threshold(5, linestyle.Light, cell.BgColor(cell.ColorRed)), + HideTextProgress(), + }, + absolute: &absoluteCall{done: 5, total: 10}, + canvas: image.Rect(0, 0, 10, 3), + want: func(size image.Point) *faketerm.Terminal { + ft := faketerm.MustNew(size) + c := testcanvas.MustNew(ft.Area()) + + testdraw.MustRectangle(c, image.Rect(0, 0, 5, 3), + draw.RectChar('o'), + draw.RectCellOpts(cell.BgColor(cell.ColorGreen)), + ) + testdraw.MustHVLines(c, []draw.HVLine{{ + Start: image.Point{X: 5, Y: 0}, + End: image.Point{X: 5, Y: 2}, + }}, draw.HVLineStyle(linestyle.Light), + draw.HVLineCellOpts(cell.BgColor(cell.ColorRed))) + testcanvas.MustApply(c, ft) + return ft + }, + }, + { + desc: "progress after threshold", + opts: []Option{ + Char('o'), + Threshold(5, linestyle.Light, cell.BgColor(cell.ColorRed)), + HideTextProgress(), + }, + absolute: &absoluteCall{done: 6, total: 10}, + canvas: image.Rect(0, 0, 10, 3), + want: func(size image.Point) *faketerm.Terminal { + ft := faketerm.MustNew(size) + c := testcanvas.MustNew(ft.Area()) + + testdraw.MustRectangle(c, image.Rect(0, 0, 6, 3), + draw.RectChar('o'), + draw.RectCellOpts(cell.BgColor(cell.ColorGreen)), + ) + testdraw.MustHVLines(c, []draw.HVLine{{ + Start: image.Point{X: 5, Y: 0}, + End: image.Point{X: 5, Y: 2}, + }}, draw.HVLineStyle(linestyle.Light), + draw.HVLineCellOpts(cell.BgColor(cell.ColorRed))) + testcanvas.MustApply(c, ft) + return ft + }, + }, } for _, tc := range tests { From 2cbce1c330492e8f753f8e135b5566e96d647c2e Mon Sep 17 00:00:00 2001 From: LQR471814 <42160088+LQR471814@users.noreply.github.com> Date: Sun, 29 Jan 2023 10:24:32 -0800 Subject: [PATCH 05/25] Add OnChange event handler to TextInput widget --- widgets/textinput/editor.go | 9 +++++++++ widgets/textinput/options.go | 14 ++++++++++++++ widgets/textinput/textinput.go | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/widgets/textinput/editor.go b/widgets/textinput/editor.go index 8df4d76..00f23bb 100644 --- a/widgets/textinput/editor.go +++ b/widgets/textinput/editor.go @@ -274,6 +274,9 @@ type fieldEditor struct { // width is the width of the text input field last time viewFor was called. width int + + // onChange if provided is the handler called when fieldData changes + onChange ChangeFn } // newFieldEditor returns a new fieldEditor instance. @@ -338,6 +341,9 @@ func (fe *fieldEditor) insert(r rune) { } fe.data.insertAt(fe.curDataPos, r) fe.curDataPos++ + if fe.onChange != nil { + fe.onChange(string(fe.data)) + } } // delete deletes the rune at the current position of the cursor. @@ -347,6 +353,9 @@ func (fe *fieldEditor) delete() { return } fe.data.deleteAt(fe.curDataPos) + if fe.onChange != nil { + fe.onChange(string(fe.data)) + } } // deleteBefore deletes the rune that is immediately to the left of the cursor. diff --git a/widgets/textinput/options.go b/widgets/textinput/options.go index 995d029..572365e 100644 --- a/widgets/textinput/options.go +++ b/widgets/textinput/options.go @@ -63,6 +63,7 @@ type options struct { filter FilterFn onSubmit SubmitFn + onChange ChangeFn clearOnSubmit bool exclusiveKeyboardOnFocus bool } @@ -269,6 +270,19 @@ func OnSubmit(fn SubmitFn) Option { }) } +// ChangeFn if provided is called when the content of the text input field changes, +// the argument data contains all the text in the field. +// +// The callback function must be thread-safe as the keyboard event that +// triggers the submission comes from a separate goroutine. +type ChangeFn func(data string) + +func OnChange(fn ChangeFn) Option { + return option(func(opts *options) { + opts.onChange = fn + }) +} + // ClearOnSubmit sets the text input to be cleared when a submit of the content // is triggered by the user pressing the Enter key. func ClearOnSubmit() Option { diff --git a/widgets/textinput/textinput.go b/widgets/textinput/textinput.go index 393e27d..7efbe80 100644 --- a/widgets/textinput/textinput.go +++ b/widgets/textinput/textinput.go @@ -73,7 +73,7 @@ func New(opts ...Option) (*TextInput, error) { editor: newFieldEditor(), opts: opt, } - + ti.editor.onChange = opt.onChange for _, r := range ti.opts.defaultText { ti.editor.insert(r) } From f28390ba72d9aa0bdee84f1bbb52aa82af6223ab Mon Sep 17 00:00:00 2001 From: LQR471814 <42160088+LQR471814@users.noreply.github.com> Date: Sun, 29 Jan 2023 11:13:48 -0800 Subject: [PATCH 06/25] Fixed an issue with ReadAndClear, Add example to textinputdemo.go, Add tests --- widgets/textinput/editor.go | 7 +- widgets/textinput/editor_test.go | 77 +++++++++++++++++++ .../textinput/textinputdemo/textinputdemo.go | 36 +++++++-- 3 files changed, 112 insertions(+), 8 deletions(-) diff --git a/widgets/textinput/editor.go b/widgets/textinput/editor.go index 00f23bb..2fb50b8 100644 --- a/widgets/textinput/editor.go +++ b/widgets/textinput/editor.go @@ -329,7 +329,12 @@ func (fe *fieldEditor) content() string { // reset resets the content back to zero. func (fe *fieldEditor) reset() { - *fe = *newFieldEditor() + newValue := newFieldEditor() + if fe.onChange != nil { + fe.onChange("") + newValue.onChange = fe.onChange + } + *fe = *newValue } // insert inserts the rune at the current position of the cursor. diff --git a/widgets/textinput/editor_test.go b/widgets/textinput/editor_test.go index 9656277..bb76ef6 100644 --- a/widgets/textinput/editor_test.go +++ b/widgets/textinput/editor_test.go @@ -330,6 +330,7 @@ func TestFieldEditor(t *testing.T) { wantContent string wantCurIdx int wantErr bool + mutations int }{ { desc: "fails for width too small", @@ -355,6 +356,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc", wantContent: "abc", wantCurIdx: 3, + mutations: 3, }, { desc: "longer data than the width, cursor at the end", @@ -369,6 +371,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cd", wantContent: "abcd", wantCurIdx: 3, + mutations: 4, }, { desc: "longer data than the width, cursor at the end, has full-width runes", @@ -383,6 +386,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦世", wantContent: "abc世", wantCurIdx: 3, + mutations: 4, }, { desc: "width decreased, adjusts cursor and shifts data", @@ -400,6 +404,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cd", wantContent: "abcd", wantCurIdx: 3, + mutations: 4, }, { desc: "cursor won't go right beyond the end of the data", @@ -417,6 +422,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cd", wantContent: "abcd", wantCurIdx: 3, + mutations: 4, }, { desc: "moves cursor to the left", @@ -435,6 +441,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cd", wantContent: "abcd", wantCurIdx: 2, + mutations: 4, }, { desc: "scrolls content to the left, start becomes visible", @@ -455,6 +462,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc⇨", wantContent: "abcd", wantCurIdx: 1, + mutations: 4, }, { desc: "scrolls content to the left, both ends invisible", @@ -476,6 +484,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cd⇨", wantContent: "abcde", wantCurIdx: 1, + mutations: 5, }, { desc: "scrolls left, then back right to make end visible again", @@ -503,6 +512,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦de", wantContent: "abcde", wantCurIdx: 3, + mutations: 5, }, { desc: "scrolls left, won't go beyond the start of data", @@ -527,6 +537,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc⇨", wantContent: "abcde", wantCurIdx: 0, + mutations: 5, }, { desc: "scrolls left, then back right won't go beyond the end of data", @@ -555,6 +566,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦de", wantContent: "abcde", wantCurIdx: 3, + mutations: 5, }, { desc: "have less data than width, all fits", @@ -571,6 +583,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc", wantContent: "abc", wantCurIdx: 3, + mutations: 3, }, { desc: "moves cursor to the start", @@ -590,6 +603,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc⇨", wantContent: "abcde", wantCurIdx: 0, + mutations: 5, }, { desc: "moves cursor to the end", @@ -613,6 +627,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦de", wantContent: "abcde", wantCurIdx: 3, + mutations: 5, }, { desc: "deletesBefore when cursor after the data", @@ -632,6 +647,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cd", wantContent: "abcd", wantCurIdx: 3, + mutations: 6, }, { desc: "deletesBefore when cursor after the data, text has full-width rune", @@ -651,6 +667,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦世", wantContent: "abc世", wantCurIdx: 3, + mutations: 6, }, { desc: "deletesBefore when cursor in the middle", @@ -676,6 +693,7 @@ func TestFieldEditor(t *testing.T) { wantView: "acd⇨", wantContent: "acde", wantCurIdx: 1, + mutations: 6, }, { desc: "deletesBefore when cursor in the middle, full-width runes", @@ -701,6 +719,7 @@ func TestFieldEditor(t *testing.T) { wantView: "世c⇨", wantContent: "世cde", wantCurIdx: 2, + mutations: 6, }, { desc: "deletesBefore does nothing when cursor at the start", @@ -724,6 +743,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc⇨", wantContent: "abcde", wantCurIdx: 0, + mutations: 5, }, { desc: "delete does nothing when cursor at the end", @@ -743,6 +763,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦de", wantContent: "abcde", wantCurIdx: 3, + mutations: 5, }, { desc: "delete in the middle, last rune remains hidden", @@ -767,6 +788,7 @@ func TestFieldEditor(t *testing.T) { wantView: "acd⇨", wantContent: "acde", wantCurIdx: 1, + mutations: 6, }, { desc: "delete in the middle, last rune becomes visible", @@ -792,6 +814,7 @@ func TestFieldEditor(t *testing.T) { wantView: "ade", wantContent: "ade", wantCurIdx: 1, + mutations: 7, }, { desc: "delete in the middle, last full-width rune would be invisible, shifts to keep cursor in window", @@ -818,6 +841,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦世", wantContent: "ab世", wantCurIdx: 1, + mutations: 7, }, { desc: "delete in the middle, last rune was and is visible", @@ -840,6 +864,7 @@ func TestFieldEditor(t *testing.T) { wantView: "ac", wantContent: "ac", wantCurIdx: 1, + mutations: 4, }, { desc: "delete in the middle, last full-width rune was and is visible", @@ -862,6 +887,7 @@ func TestFieldEditor(t *testing.T) { wantView: "a世", wantContent: "a世", wantCurIdx: 1, + mutations: 4, }, { desc: "delete last rune, contains full-width runes", @@ -885,6 +911,7 @@ func TestFieldEditor(t *testing.T) { wantView: "", wantContent: "", wantCurIdx: 0, + mutations: 6, }, { desc: "half-width runes only, exact fit", @@ -901,6 +928,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc", wantContent: "abc", wantCurIdx: 3, + mutations: 3, }, { desc: "full-width runes only, exact fit", @@ -917,6 +945,7 @@ func TestFieldEditor(t *testing.T) { wantView: "你好世", wantContent: "你好世", wantCurIdx: 6, + mutations: 3, }, { desc: "half-width runes only, both ends hidden", @@ -938,6 +967,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cd⇨", wantContent: "abcde", wantCurIdx: 1, + mutations: 5, }, { desc: "half-width runes only, both ends invisible, scrolls to make start visible", @@ -960,6 +990,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc⇨", wantContent: "abcde", wantCurIdx: 1, + mutations: 5, }, { desc: "half-width runes only, both ends invisible, deletes to make start visible", @@ -982,6 +1013,7 @@ func TestFieldEditor(t *testing.T) { wantView: "acd⇨", wantContent: "acde", wantCurIdx: 1, + mutations: 6, }, { desc: "half-width runes only, deletion on second page refills the field", @@ -1004,6 +1036,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦df", wantContent: "abcdf", wantCurIdx: 2, + mutations: 7, }, { desc: "half-width runes only, both ends invisible, scrolls to make end visible", @@ -1030,6 +1063,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦de", wantContent: "abcde", wantCurIdx: 2, + mutations: 5, }, { desc: "half-width runes only, both ends invisible, deletes to make end visible", @@ -1055,6 +1089,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦de", wantContent: "abde", wantCurIdx: 1, + mutations: 6, }, { desc: "full-width runes only, both ends invisible", @@ -1074,6 +1109,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦⇦世⇨", wantContent: "你好世界", wantCurIdx: 2, + mutations: 4, }, { desc: "full-width runes only, both ends invisible, scrolls to make start visible", @@ -1097,6 +1133,7 @@ func TestFieldEditor(t *testing.T) { wantView: "你好⇨", wantContent: "你好世界", wantCurIdx: 2, + mutations: 4, }, { desc: "full-width runes only, both ends invisible, deletes to make start visible", @@ -1120,6 +1157,7 @@ func TestFieldEditor(t *testing.T) { wantView: "你世⇨", wantContent: "你世界", wantCurIdx: 2, + mutations: 5, }, { desc: "full-width runes only, both ends invisible, scrolls to make end visible", @@ -1143,6 +1181,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦⇦界", wantContent: "你好世界", wantCurIdx: 2, + mutations: 4, }, { desc: "full-width runes only, both ends invisible, deletes to make end visible", @@ -1166,6 +1205,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦⇦界", wantContent: "你好界", wantCurIdx: 2, + mutations: 5, }, { desc: "scrolls to make full-width rune appear at the beginning", @@ -1186,6 +1226,7 @@ func TestFieldEditor(t *testing.T) { wantView: "你b⇨", wantContent: "你bcd", wantCurIdx: 2, + mutations: 4, }, { desc: "scrolls to make full-width rune appear at the end", @@ -1207,6 +1248,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦你", wantContent: "abc你", wantCurIdx: 1, + mutations: 4, }, { desc: "inserts after last full width rune, first is half-width", @@ -1225,6 +1267,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦c你e", wantContent: "abc你e", wantCurIdx: 5, + mutations: 5, }, { desc: "inserts after last full width rune, first is half-width", @@ -1242,6 +1285,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦你d", wantContent: "世b你d", wantCurIdx: 4, + mutations: 4, }, { desc: "inserts after last full width rune, hidden rune is full-width", @@ -1259,6 +1303,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦⇦cd", wantContent: "世你cd", wantCurIdx: 4, + mutations: 4, }, { desc: "scrolls right, first is full-width, last are half-width", @@ -1285,6 +1330,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦⇦def⇨", wantContent: "a你世defgh", wantCurIdx: 3, + mutations: 8, }, { desc: "scrolls right, first is half-width, last is full-width", @@ -1311,6 +1357,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦你世⇨", wantContent: "abc你世fgh", wantCurIdx: 3, + mutations: 8, }, { desc: "scrolls right, first and last are full-width", @@ -1331,6 +1378,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦⇦世⇨", wantContent: "你好世界", wantCurIdx: 2, + mutations: 4, }, { desc: "scrolls right, first and last are half-width", @@ -1357,6 +1405,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cdef⇨", wantContent: "abcdefg", wantCurIdx: 4, + mutations: 7, }, { desc: "scrolls left, first is full-width, last are half-width", @@ -1383,6 +1432,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦⇦def⇨", wantContent: "a你世defgh", wantCurIdx: 2, + mutations: 8, }, { desc: "scrolls left, first is half-width, last is full-width", @@ -1409,6 +1459,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦你世⇨", wantContent: "abc你世fgh", wantCurIdx: 1, + mutations: 8, }, { desc: "scrolls left, first and last are full-width", @@ -1428,6 +1479,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦⇦世⇨", wantContent: "你好世界", wantCurIdx: 2, + mutations: 4, }, { desc: "scrolls left, first and last are half-width", @@ -1453,6 +1505,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cdef⇨", wantContent: "abcdefg", wantCurIdx: 1, + mutations: 7, }, { desc: "resets the field editor", @@ -1470,6 +1523,7 @@ func TestFieldEditor(t *testing.T) { wantView: "", wantContent: "", wantCurIdx: 0, + mutations: 3, }, { desc: "doesn't insert runes with rune width of zero", @@ -1486,6 +1540,7 @@ func TestFieldEditor(t *testing.T) { wantView: "ac", wantContent: "ac", wantCurIdx: 2, + mutations: 2, }, { desc: "all text visible, moves cursor to position zero", @@ -1503,6 +1558,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc", wantContent: "abc", wantCurIdx: 0, + mutations: 3, }, { desc: "all text visible, moves cursor to position in the middle", @@ -1520,6 +1576,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc", wantContent: "abc", wantCurIdx: 1, + mutations: 3, }, { desc: "all text visible, moves cursor back to the last character", @@ -1538,6 +1595,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc", wantContent: "abc", wantCurIdx: 2, + mutations: 3, }, { desc: "all text visible, moves cursor to the appending space", @@ -1556,6 +1614,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc", wantContent: "abc", wantCurIdx: 3, + mutations: 3, }, { desc: "all text visible, moves cursor before the beginning of data", @@ -1574,6 +1633,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc", wantContent: "abc", wantCurIdx: 0, + mutations: 3, }, { desc: "all text visible, moves cursor after the appending space", @@ -1592,6 +1652,7 @@ func TestFieldEditor(t *testing.T) { wantView: "abc", wantContent: "abc", wantCurIdx: 3, + mutations: 3, }, { desc: "moves cursor when there is no text", @@ -1629,6 +1690,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cd⇨", wantContent: "abcde", wantCurIdx: 1, + mutations: 5, }, { desc: "both ends hidden, moves cursor onto the first character", @@ -1655,6 +1717,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cd⇨", wantContent: "abcde", wantCurIdx: 1, + mutations: 5, }, { desc: "both ends hidden, moves cursor onto the right arrow", @@ -1680,6 +1743,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cd⇨", wantContent: "abcde", wantCurIdx: 2, + mutations: 5, }, { desc: "both ends hidden, moves cursor onto the last character", @@ -1705,6 +1769,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦cd⇨", wantContent: "abcde", wantCurIdx: 2, + mutations: 5, }, { desc: "moves cursor onto the first cell containing a full-width rune", @@ -1730,6 +1795,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦⇦世界⇨", wantContent: "你好世界你", wantCurIdx: 4, + mutations: 5, }, { desc: "moves cursor onto the second cell containing a full-width rune", @@ -1755,6 +1821,7 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦⇦世界⇨", wantContent: "你好世界你", wantCurIdx: 4, + mutations: 5, }, { desc: "moves cursor onto the second right arrow", @@ -1780,12 +1847,18 @@ func TestFieldEditor(t *testing.T) { wantView: "⇦⇦世界⇨", wantContent: "你好世界你", wantCurIdx: 2, + mutations: 5, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { fe := newFieldEditor() + var changeCount int + fe.onChange = func(data string) { + changeCount++ + } + if tc.ops != nil { if err := tc.ops(fe); err != nil { t.Fatalf("ops => unexpected error: %v", err) @@ -1808,6 +1881,10 @@ func TestFieldEditor(t *testing.T) { if gotContent != tc.wantContent { t.Errorf("content -> %q, want %q", gotContent, tc.wantContent) } + + if tc.mutations != changeCount { + t.Errorf("mutation count -> %d, want %d", changeCount, tc.mutations) + } }) } } diff --git a/widgets/textinput/textinputdemo/textinputdemo.go b/widgets/textinput/textinputdemo/textinputdemo.go index d4d468c..1d73436 100644 --- a/widgets/textinput/textinputdemo/textinputdemo.go +++ b/widgets/textinput/textinputdemo/textinputdemo.go @@ -29,6 +29,7 @@ import ( "github.com/mum4k/termdash/terminal/tcell" "github.com/mum4k/termdash/widgets/button" "github.com/mum4k/termdash/widgets/segmentdisplay" + "github.com/mum4k/termdash/widgets/text" "github.com/mum4k/termdash/widgets/textinput" ) @@ -126,11 +127,20 @@ func main() { updateText := make(chan string) go rollText(ctx, rollingSD, updateText) + mirror, err := text.New() + if err != nil { + panic(err) + } + input, err := textinput.New( textinput.Label("New text:", cell.FgColor(cell.ColorNumber(33))), textinput.MaxWidthCells(20), textinput.Border(linestyle.Light), textinput.PlaceHolder("Enter any text"), + textinput.OnChange(func(data string) { + mirror.Reset() + mirror.Write(data) + }), ) if err != nil { panic(err) @@ -177,18 +187,30 @@ func main() { ), ) builder.Add( - grid.RowHeightPerc(20, - grid.Widget( - input, - container.AlignHorizontal(align.HorizontalCenter), - container.AlignVertical(align.VerticalBottom), - container.MarginBottom(1), + grid.RowHeightPerc(40, + grid.RowHeightPerc(50, + grid.Widget( + input, + container.AlignHorizontal(align.HorizontalCenter), + container.AlignVertical(align.VerticalBottom), + container.MarginBottom(1), + ), + ), + grid.RowHeightPerc(50, + grid.Widget( + mirror, + container.Border(linestyle.Light), + container.BorderTitle("text"), + container.AlignHorizontal(align.HorizontalCenter), + container.AlignHorizontal(align.Horizontal(align.VerticalBottom)), + container.MarginBottom(1), + ), ), ), ) builder.Add( - grid.RowHeightPerc(40, + grid.RowHeightPerc(20, grid.ColWidthPerc(20), grid.ColWidthPerc(20, grid.Widget( From e87b1cb791a7ae9e5bd88ddbe9aa3df1bfa96703 Mon Sep 17 00:00:00 2001 From: LQR471814 <42160088+LQR471814@users.noreply.github.com> Date: Sun, 29 Jan 2023 11:15:42 -0800 Subject: [PATCH 07/25] capitalize text --- widgets/textinput/textinputdemo/textinputdemo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets/textinput/textinputdemo/textinputdemo.go b/widgets/textinput/textinputdemo/textinputdemo.go index 1d73436..ce44056 100644 --- a/widgets/textinput/textinputdemo/textinputdemo.go +++ b/widgets/textinput/textinputdemo/textinputdemo.go @@ -200,7 +200,7 @@ func main() { grid.Widget( mirror, container.Border(linestyle.Light), - container.BorderTitle("text"), + container.BorderTitle("Text"), container.AlignHorizontal(align.HorizontalCenter), container.AlignHorizontal(align.Horizontal(align.VerticalBottom)), container.MarginBottom(1), From 45f26a878ddc151888c461412c83ed319618cac1 Mon Sep 17 00:00:00 2001 From: LQR471814 <42160088+LQR471814@users.noreply.github.com> Date: Mon, 30 Jan 2023 17:56:04 -0800 Subject: [PATCH 08/25] address comments --- widgets/textinput/editor.go | 11 +- widgets/textinput/editor_test.go | 563 +++++++++++++++---------------- widgets/textinput/options.go | 5 +- widgets/textinput/textinput.go | 3 +- 4 files changed, 288 insertions(+), 294 deletions(-) diff --git a/widgets/textinput/editor.go b/widgets/textinput/editor.go index 2fb50b8..63df1d1 100644 --- a/widgets/textinput/editor.go +++ b/widgets/textinput/editor.go @@ -280,8 +280,8 @@ type fieldEditor struct { } // newFieldEditor returns a new fieldEditor instance. -func newFieldEditor() *fieldEditor { - return &fieldEditor{} +func newFieldEditor(onChange ChangeFn) *fieldEditor { + return &fieldEditor{onChange: onChange} } // minFieldWidth is the minimum supported width of the text input field. @@ -329,12 +329,7 @@ func (fe *fieldEditor) content() string { // reset resets the content back to zero. func (fe *fieldEditor) reset() { - newValue := newFieldEditor() - if fe.onChange != nil { - fe.onChange("") - newValue.onChange = fe.onChange - } - *fe = *newValue + *fe = *newFieldEditor(fe.onChange) } // insert inserts the rune at the current position of the cursor. diff --git a/widgets/textinput/editor_test.go b/widgets/textinput/editor_test.go index bb76ef6..89e6937 100644 --- a/widgets/textinput/editor_test.go +++ b/widgets/textinput/editor_test.go @@ -309,7 +309,7 @@ func TestCurCell(t *testing.T) { for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { - fe := newFieldEditor() + fe := newFieldEditor(nil) fe.data = tc.data fe.firstRune = tc.firstRune fe.curDataPos = tc.curDataPos @@ -323,14 +323,14 @@ func TestCurCell(t *testing.T) { func TestFieldEditor(t *testing.T) { tests := []struct { - desc string - width int - ops func(*fieldEditor) error - wantView string - wantContent string - wantCurIdx int - wantErr bool - mutations int + desc string + width int + ops func(*fieldEditor) error + wantView string + wantContent string + wantCurIdx int + wantErr bool + wantOnChangeCalls int }{ { desc: "fails for width too small", @@ -353,10 +353,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('c') return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 3, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 3, + wantOnChangeCalls: 3, }, { desc: "longer data than the width, cursor at the end", @@ -368,10 +368,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('d') return nil }, - wantView: "⇦cd", - wantContent: "abcd", - wantCurIdx: 3, - mutations: 4, + wantView: "⇦cd", + wantContent: "abcd", + wantCurIdx: 3, + wantOnChangeCalls: 4, }, { desc: "longer data than the width, cursor at the end, has full-width runes", @@ -383,10 +383,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('世') return nil }, - wantView: "⇦世", - wantContent: "abc世", - wantCurIdx: 3, - mutations: 4, + wantView: "⇦世", + wantContent: "abc世", + wantCurIdx: 3, + wantOnChangeCalls: 4, }, { desc: "width decreased, adjusts cursor and shifts data", @@ -401,10 +401,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('d') return nil }, - wantView: "⇦cd", - wantContent: "abcd", - wantCurIdx: 3, - mutations: 4, + wantView: "⇦cd", + wantContent: "abcd", + wantCurIdx: 3, + wantOnChangeCalls: 4, }, { desc: "cursor won't go right beyond the end of the data", @@ -419,10 +419,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦cd", - wantContent: "abcd", - wantCurIdx: 3, - mutations: 4, + wantView: "⇦cd", + wantContent: "abcd", + wantCurIdx: 3, + wantOnChangeCalls: 4, }, { desc: "moves cursor to the left", @@ -438,10 +438,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦cd", - wantContent: "abcd", - wantCurIdx: 2, - mutations: 4, + wantView: "⇦cd", + wantContent: "abcd", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "scrolls content to the left, start becomes visible", @@ -459,10 +459,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "abc⇨", - wantContent: "abcd", - wantCurIdx: 1, - mutations: 4, + wantView: "abc⇨", + wantContent: "abcd", + wantCurIdx: 1, + wantOnChangeCalls: 4, }, { desc: "scrolls content to the left, both ends invisible", @@ -481,10 +481,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦cd⇨", - wantContent: "abcde", - wantCurIdx: 1, - mutations: 5, + wantView: "⇦cd⇨", + wantContent: "abcde", + wantCurIdx: 1, + wantOnChangeCalls: 5, }, { desc: "scrolls left, then back right to make end visible again", @@ -509,10 +509,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦de", - wantContent: "abcde", - wantCurIdx: 3, - mutations: 5, + wantView: "⇦de", + wantContent: "abcde", + wantCurIdx: 3, + wantOnChangeCalls: 5, }, { desc: "scrolls left, won't go beyond the start of data", @@ -534,10 +534,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "abc⇨", - wantContent: "abcde", - wantCurIdx: 0, - mutations: 5, + wantView: "abc⇨", + wantContent: "abcde", + wantCurIdx: 0, + wantOnChangeCalls: 5, }, { desc: "scrolls left, then back right won't go beyond the end of data", @@ -563,10 +563,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦de", - wantContent: "abcde", - wantCurIdx: 3, - mutations: 5, + wantView: "⇦de", + wantContent: "abcde", + wantCurIdx: 3, + wantOnChangeCalls: 5, }, { desc: "have less data than width, all fits", @@ -580,10 +580,10 @@ func TestFieldEditor(t *testing.T) { } return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 3, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 3, + wantOnChangeCalls: 3, }, { desc: "moves cursor to the start", @@ -600,10 +600,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorStart() return nil }, - wantView: "abc⇨", - wantContent: "abcde", - wantCurIdx: 0, - mutations: 5, + wantView: "abc⇨", + wantContent: "abcde", + wantCurIdx: 0, + wantOnChangeCalls: 5, }, { desc: "moves cursor to the end", @@ -624,10 +624,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorEnd() return nil }, - wantView: "⇦de", - wantContent: "abcde", - wantCurIdx: 3, - mutations: 5, + wantView: "⇦de", + wantContent: "abcde", + wantCurIdx: 3, + wantOnChangeCalls: 5, }, { desc: "deletesBefore when cursor after the data", @@ -644,10 +644,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "⇦cd", - wantContent: "abcd", - wantCurIdx: 3, - mutations: 6, + wantView: "⇦cd", + wantContent: "abcd", + wantCurIdx: 3, + wantOnChangeCalls: 6, }, { desc: "deletesBefore when cursor after the data, text has full-width rune", @@ -664,10 +664,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "⇦世", - wantContent: "abc世", - wantCurIdx: 3, - mutations: 6, + wantView: "⇦世", + wantContent: "abc世", + wantCurIdx: 3, + wantOnChangeCalls: 6, }, { desc: "deletesBefore when cursor in the middle", @@ -690,10 +690,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "acd⇨", - wantContent: "acde", - wantCurIdx: 1, - mutations: 6, + wantView: "acd⇨", + wantContent: "acde", + wantCurIdx: 1, + wantOnChangeCalls: 6, }, { desc: "deletesBefore when cursor in the middle, full-width runes", @@ -716,10 +716,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "世c⇨", - wantContent: "世cde", - wantCurIdx: 2, - mutations: 6, + wantView: "世c⇨", + wantContent: "世cde", + wantCurIdx: 2, + wantOnChangeCalls: 6, }, { desc: "deletesBefore does nothing when cursor at the start", @@ -740,10 +740,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "abc⇨", - wantContent: "abcde", - wantCurIdx: 0, - mutations: 5, + wantView: "abc⇨", + wantContent: "abcde", + wantCurIdx: 0, + wantOnChangeCalls: 5, }, { desc: "delete does nothing when cursor at the end", @@ -760,10 +760,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "⇦de", - wantContent: "abcde", - wantCurIdx: 3, - mutations: 5, + wantView: "⇦de", + wantContent: "abcde", + wantCurIdx: 3, + wantOnChangeCalls: 5, }, { desc: "delete in the middle, last rune remains hidden", @@ -785,10 +785,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "acd⇨", - wantContent: "acde", - wantCurIdx: 1, - mutations: 6, + wantView: "acd⇨", + wantContent: "acde", + wantCurIdx: 1, + wantOnChangeCalls: 6, }, { desc: "delete in the middle, last rune becomes visible", @@ -811,10 +811,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "ade", - wantContent: "ade", - wantCurIdx: 1, - mutations: 7, + wantView: "ade", + wantContent: "ade", + wantCurIdx: 1, + wantOnChangeCalls: 7, }, { desc: "delete in the middle, last full-width rune would be invisible, shifts to keep cursor in window", @@ -838,10 +838,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "⇦世", - wantContent: "ab世", - wantCurIdx: 1, - mutations: 7, + wantView: "⇦世", + wantContent: "ab世", + wantCurIdx: 1, + wantOnChangeCalls: 7, }, { desc: "delete in the middle, last rune was and is visible", @@ -861,10 +861,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "ac", - wantContent: "ac", - wantCurIdx: 1, - mutations: 4, + wantView: "ac", + wantContent: "ac", + wantCurIdx: 1, + wantOnChangeCalls: 4, }, { desc: "delete in the middle, last full-width rune was and is visible", @@ -884,10 +884,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "a世", - wantContent: "a世", - wantCurIdx: 1, - mutations: 4, + wantView: "a世", + wantContent: "a世", + wantCurIdx: 1, + wantOnChangeCalls: 4, }, { desc: "delete last rune, contains full-width runes", @@ -908,10 +908,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "", - wantContent: "", - wantCurIdx: 0, - mutations: 6, + wantView: "", + wantContent: "", + wantCurIdx: 0, + wantOnChangeCalls: 6, }, { desc: "half-width runes only, exact fit", @@ -925,10 +925,10 @@ func TestFieldEditor(t *testing.T) { } return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 3, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 3, + wantOnChangeCalls: 3, }, { desc: "full-width runes only, exact fit", @@ -942,10 +942,10 @@ func TestFieldEditor(t *testing.T) { } return nil }, - wantView: "你好世", - wantContent: "你好世", - wantCurIdx: 6, - mutations: 3, + wantView: "你好世", + wantContent: "你好世", + wantCurIdx: 6, + wantOnChangeCalls: 3, }, { desc: "half-width runes only, both ends hidden", @@ -964,10 +964,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦cd⇨", - wantContent: "abcde", - wantCurIdx: 1, - mutations: 5, + wantView: "⇦cd⇨", + wantContent: "abcde", + wantCurIdx: 1, + wantOnChangeCalls: 5, }, { desc: "half-width runes only, both ends invisible, scrolls to make start visible", @@ -987,10 +987,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "abc⇨", - wantContent: "abcde", - wantCurIdx: 1, - mutations: 5, + wantView: "abc⇨", + wantContent: "abcde", + wantCurIdx: 1, + wantOnChangeCalls: 5, }, { desc: "half-width runes only, both ends invisible, deletes to make start visible", @@ -1010,10 +1010,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "acd⇨", - wantContent: "acde", - wantCurIdx: 1, - mutations: 6, + wantView: "acd⇨", + wantContent: "acde", + wantCurIdx: 1, + wantOnChangeCalls: 6, }, { desc: "half-width runes only, deletion on second page refills the field", @@ -1033,10 +1033,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "⇦df", - wantContent: "abcdf", - wantCurIdx: 2, - mutations: 7, + wantView: "⇦df", + wantContent: "abcdf", + wantCurIdx: 2, + wantOnChangeCalls: 7, }, { desc: "half-width runes only, both ends invisible, scrolls to make end visible", @@ -1060,10 +1060,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦de", - wantContent: "abcde", - wantCurIdx: 2, - mutations: 5, + wantView: "⇦de", + wantContent: "abcde", + wantCurIdx: 2, + wantOnChangeCalls: 5, }, { desc: "half-width runes only, both ends invisible, deletes to make end visible", @@ -1086,10 +1086,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "⇦de", - wantContent: "abde", - wantCurIdx: 1, - mutations: 6, + wantView: "⇦de", + wantContent: "abde", + wantCurIdx: 1, + wantOnChangeCalls: 6, }, { desc: "full-width runes only, both ends invisible", @@ -1106,10 +1106,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦⇦世⇨", - wantContent: "你好世界", - wantCurIdx: 2, - mutations: 4, + wantView: "⇦⇦世⇨", + wantContent: "你好世界", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "full-width runes only, both ends invisible, scrolls to make start visible", @@ -1130,10 +1130,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "你好⇨", - wantContent: "你好世界", - wantCurIdx: 2, - mutations: 4, + wantView: "你好⇨", + wantContent: "你好世界", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "full-width runes only, both ends invisible, deletes to make start visible", @@ -1154,10 +1154,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "你世⇨", - wantContent: "你世界", - wantCurIdx: 2, - mutations: 5, + wantView: "你世⇨", + wantContent: "你世界", + wantCurIdx: 2, + wantOnChangeCalls: 5, }, { desc: "full-width runes only, both ends invisible, scrolls to make end visible", @@ -1178,10 +1178,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦⇦界", - wantContent: "你好世界", - wantCurIdx: 2, - mutations: 4, + wantView: "⇦⇦界", + wantContent: "你好世界", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "full-width runes only, both ends invisible, deletes to make end visible", @@ -1202,10 +1202,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "⇦⇦界", - wantContent: "你好界", - wantCurIdx: 2, - mutations: 5, + wantView: "⇦⇦界", + wantContent: "你好界", + wantCurIdx: 2, + wantOnChangeCalls: 5, }, { desc: "scrolls to make full-width rune appear at the beginning", @@ -1223,10 +1223,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "你b⇨", - wantContent: "你bcd", - wantCurIdx: 2, - mutations: 4, + wantView: "你b⇨", + wantContent: "你bcd", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "scrolls to make full-width rune appear at the end", @@ -1245,10 +1245,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦你", - wantContent: "abc你", - wantCurIdx: 1, - mutations: 4, + wantView: "⇦你", + wantContent: "abc你", + wantCurIdx: 1, + wantOnChangeCalls: 4, }, { desc: "inserts after last full width rune, first is half-width", @@ -1264,10 +1264,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('e') return nil }, - wantView: "⇦c你e", - wantContent: "abc你e", - wantCurIdx: 5, - mutations: 5, + wantView: "⇦c你e", + wantContent: "abc你e", + wantCurIdx: 5, + wantOnChangeCalls: 5, }, { desc: "inserts after last full width rune, first is half-width", @@ -1282,10 +1282,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('d') return nil }, - wantView: "⇦你d", - wantContent: "世b你d", - wantCurIdx: 4, - mutations: 4, + wantView: "⇦你d", + wantContent: "世b你d", + wantCurIdx: 4, + wantOnChangeCalls: 4, }, { desc: "inserts after last full width rune, hidden rune is full-width", @@ -1300,10 +1300,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('d') return nil }, - wantView: "⇦⇦cd", - wantContent: "世你cd", - wantCurIdx: 4, - mutations: 4, + wantView: "⇦⇦cd", + wantContent: "世你cd", + wantCurIdx: 4, + wantOnChangeCalls: 4, }, { desc: "scrolls right, first is full-width, last are half-width", @@ -1327,10 +1327,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦⇦def⇨", - wantContent: "a你世defgh", - wantCurIdx: 3, - mutations: 8, + wantView: "⇦⇦def⇨", + wantContent: "a你世defgh", + wantCurIdx: 3, + wantOnChangeCalls: 8, }, { desc: "scrolls right, first is half-width, last is full-width", @@ -1354,10 +1354,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦你世⇨", - wantContent: "abc你世fgh", - wantCurIdx: 3, - mutations: 8, + wantView: "⇦你世⇨", + wantContent: "abc你世fgh", + wantCurIdx: 3, + wantOnChangeCalls: 8, }, { desc: "scrolls right, first and last are full-width", @@ -1375,10 +1375,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦⇦世⇨", - wantContent: "你好世界", - wantCurIdx: 2, - mutations: 4, + wantView: "⇦⇦世⇨", + wantContent: "你好世界", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "scrolls right, first and last are half-width", @@ -1402,10 +1402,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦cdef⇨", - wantContent: "abcdefg", - wantCurIdx: 4, - mutations: 7, + wantView: "⇦cdef⇨", + wantContent: "abcdefg", + wantCurIdx: 4, + wantOnChangeCalls: 7, }, { desc: "scrolls left, first is full-width, last are half-width", @@ -1429,10 +1429,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦⇦def⇨", - wantContent: "a你世defgh", - wantCurIdx: 2, - mutations: 8, + wantView: "⇦⇦def⇨", + wantContent: "a你世defgh", + wantCurIdx: 2, + wantOnChangeCalls: 8, }, { desc: "scrolls left, first is half-width, last is full-width", @@ -1456,10 +1456,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦你世⇨", - wantContent: "abc你世fgh", - wantCurIdx: 1, - mutations: 8, + wantView: "⇦你世⇨", + wantContent: "abc你世fgh", + wantCurIdx: 1, + wantOnChangeCalls: 8, }, { desc: "scrolls left, first and last are full-width", @@ -1476,10 +1476,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦⇦世⇨", - wantContent: "你好世界", - wantCurIdx: 2, - mutations: 4, + wantView: "⇦⇦世⇨", + wantContent: "你好世界", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "scrolls left, first and last are half-width", @@ -1502,10 +1502,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦cdef⇨", - wantContent: "abcdefg", - wantCurIdx: 1, - mutations: 7, + wantView: "⇦cdef⇨", + wantContent: "abcdefg", + wantCurIdx: 1, + wantOnChangeCalls: 7, }, { desc: "resets the field editor", @@ -1520,10 +1520,10 @@ func TestFieldEditor(t *testing.T) { fe.reset() return nil }, - wantView: "", - wantContent: "", - wantCurIdx: 0, - mutations: 3, + wantView: "", + wantContent: "", + wantCurIdx: 0, + wantOnChangeCalls: 4, }, { desc: "doesn't insert runes with rune width of zero", @@ -1537,10 +1537,10 @@ func TestFieldEditor(t *testing.T) { } return nil }, - wantView: "ac", - wantContent: "ac", - wantCurIdx: 2, - mutations: 2, + wantView: "ac", + wantContent: "ac", + wantCurIdx: 2, + wantOnChangeCalls: 2, }, { desc: "all text visible, moves cursor to position zero", @@ -1555,10 +1555,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(0) return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 0, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 0, + wantOnChangeCalls: 3, }, { desc: "all text visible, moves cursor to position in the middle", @@ -1573,10 +1573,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(1) return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 1, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 1, + wantOnChangeCalls: 3, }, { desc: "all text visible, moves cursor back to the last character", @@ -1592,10 +1592,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(2) return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 2, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 2, + wantOnChangeCalls: 3, }, { desc: "all text visible, moves cursor to the appending space", @@ -1611,10 +1611,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(3) return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 3, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 3, + wantOnChangeCalls: 3, }, { desc: "all text visible, moves cursor before the beginning of data", @@ -1630,10 +1630,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(-1) return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 0, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 0, + wantOnChangeCalls: 3, }, { desc: "all text visible, moves cursor after the appending space", @@ -1649,10 +1649,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(10) return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 3, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 3, + wantOnChangeCalls: 3, }, { desc: "moves cursor when there is no text", @@ -1687,10 +1687,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(0) return nil }, - wantView: "⇦cd⇨", - wantContent: "abcde", - wantCurIdx: 1, - mutations: 5, + wantView: "⇦cd⇨", + wantContent: "abcde", + wantCurIdx: 1, + wantOnChangeCalls: 5, }, { desc: "both ends hidden, moves cursor onto the first character", @@ -1714,10 +1714,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(1) return nil }, - wantView: "⇦cd⇨", - wantContent: "abcde", - wantCurIdx: 1, - mutations: 5, + wantView: "⇦cd⇨", + wantContent: "abcde", + wantCurIdx: 1, + wantOnChangeCalls: 5, }, { desc: "both ends hidden, moves cursor onto the right arrow", @@ -1740,10 +1740,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(3) return nil }, - wantView: "⇦cd⇨", - wantContent: "abcde", - wantCurIdx: 2, - mutations: 5, + wantView: "⇦cd⇨", + wantContent: "abcde", + wantCurIdx: 2, + wantOnChangeCalls: 5, }, { desc: "both ends hidden, moves cursor onto the last character", @@ -1766,10 +1766,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(2) return nil }, - wantView: "⇦cd⇨", - wantContent: "abcde", - wantCurIdx: 2, - mutations: 5, + wantView: "⇦cd⇨", + wantContent: "abcde", + wantCurIdx: 2, + wantOnChangeCalls: 5, }, { desc: "moves cursor onto the first cell containing a full-width rune", @@ -1792,10 +1792,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(4) return nil }, - wantView: "⇦⇦世界⇨", - wantContent: "你好世界你", - wantCurIdx: 4, - mutations: 5, + wantView: "⇦⇦世界⇨", + wantContent: "你好世界你", + wantCurIdx: 4, + wantOnChangeCalls: 5, }, { desc: "moves cursor onto the second cell containing a full-width rune", @@ -1818,10 +1818,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(5) return nil }, - wantView: "⇦⇦世界⇨", - wantContent: "你好世界你", - wantCurIdx: 4, - mutations: 5, + wantView: "⇦⇦世界⇨", + wantContent: "你好世界你", + wantCurIdx: 4, + wantOnChangeCalls: 5, }, { desc: "moves cursor onto the second right arrow", @@ -1844,20 +1844,19 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(1) return nil }, - wantView: "⇦⇦世界⇨", - wantContent: "你好世界你", - wantCurIdx: 2, - mutations: 5, + wantView: "⇦⇦世界⇨", + wantContent: "你好世界你", + wantCurIdx: 2, + wantOnChangeCalls: 5, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { - fe := newFieldEditor() var changeCount int - fe.onChange = func(data string) { + fe := newFieldEditor(func(data string) { changeCount++ - } + }) if tc.ops != nil { if err := tc.ops(fe); err != nil { @@ -1882,8 +1881,8 @@ func TestFieldEditor(t *testing.T) { t.Errorf("content -> %q, want %q", gotContent, tc.wantContent) } - if tc.mutations != changeCount { - t.Errorf("mutation count -> %d, want %d", changeCount, tc.mutations) + if tc.wantOnChangeCalls != changeCount { + t.Errorf("unexpected number of onChange calls -> %d, want %d", changeCount, tc.wantOnChangeCalls) } }) } diff --git a/widgets/textinput/options.go b/widgets/textinput/options.go index 572365e..2ff1103 100644 --- a/widgets/textinput/options.go +++ b/widgets/textinput/options.go @@ -270,13 +270,14 @@ func OnSubmit(fn SubmitFn) Option { }) } -// ChangeFn if provided is called when the content of the text input field changes, -// the argument data contains all the text in the field. +// The argument to ChangeFn contains all the text in the field after the change. // // The callback function must be thread-safe as the keyboard event that // triggers the submission comes from a separate goroutine. type ChangeFn func(data string) +// OnChange sets a function that will be called when the content of the text input +// field changes. func OnChange(fn ChangeFn) Option { return option(func(opts *options) { opts.onChange = fn diff --git a/widgets/textinput/textinput.go b/widgets/textinput/textinput.go index 7efbe80..ee4c0d3 100644 --- a/widgets/textinput/textinput.go +++ b/widgets/textinput/textinput.go @@ -70,10 +70,9 @@ func New(opts ...Option) (*TextInput, error) { return nil, err } ti := &TextInput{ - editor: newFieldEditor(), + editor: newFieldEditor(opt.onChange), opts: opt, } - ti.editor.onChange = opt.onChange for _, r := range ti.opts.defaultText { ti.editor.insert(r) } From 3affaaa53b7522982e62abe8fcc6a675cb16284e Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 12:46:58 -0500 Subject: [PATCH 09/25] Temporarily disable line coverage. Signed-off-by: Jakub Sobon --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index cd62bee..ae633c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,8 +18,6 @@ script: - diff -u <(echo -n) <(gofmt -d -s .) - diff -u <(echo -n) <(./private/scripts/autogen_licences.sh .) - diff -u <(echo -n) <(golint ./...) - - $GOPATH/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN env: global: - CGO_ENABLED=0 - - secure: VOOh/w2YNAn+psiWYjIOQ5ZhhMb6Wz7zzmhIlj0dc5mGQztFAX5TuNWOU5JokvOigFy18JhPeDJRmp661xqM4gy1Znx1odSXES3YdCwt42pmpjYIkI9lI09xTRH6WYIRmYfCHe4J3A9/CWLeDRaAU1e+YqmNyraaGzE82ouUPH/I4A9gur4j4j6t1X/t0iovyd/4qNDsetUPevQsJS224Pv6Xhg3LGnSAXMPM+tu0t3UeEfRu/l9OgP6/bnet9BUx0BryFCVJp6fAtq7x61+WRIJesugrhHVgl/dz8CgFsVjRkqWQSNnZvt07dHNOX0mZj2U22OAkH+9ZN93wScs3bDZFXozrta7eOWhrJLcJTMrAxdHYMNKmoXqQQ0TGFV/L9blOtT8uj9US3wxeD11s4TyZePWIC5hnpUsNFoGPsBB45uwW2TSwvTTEL9bxWWzjYzSkLG5P6Kk4/JkeMh3OMFCM/LutX8QDch1n/s0CfXdy7qgh5G4I9ZhGTU+huJlumeuM4U+my0EPnA3uclJ97cw0n6K7MKwKCTTA8La2ifATunKC/U4Hjo1rf9DxofIrRIvwV5zEUIn1r6ut5fO+o+MWDupkvsMqIA7QJyCLhRp+pAlPWGDZLdrFEicN/kpULH4IGUIPn532gXzEOAG+Aq0WYDVPXGLSifSyxafiDk= From 0f346f34d1d14cd8c53cd2060518dff283dcb845 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 13:01:46 -0500 Subject: [PATCH 10/25] Modifying CHANGELOG and README. Signed-off-by: Jakub Sobon --- CHANGELOG.md | 17 +++++++++++++++++ README.md | 1 - 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3265977..c1b9026 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- The `gauge` widget now supports drawing of a vertical threshold bar. +- The `TextInput` widget now supports an OnChange handler that allows user code + to be notified when the content of the text input changes. + +### Changed + +- Bump github.com/gdamore/tcell/v2 from 2.5.1 to 2.5.4. +- Bump github.com/mattn/go-runewidth from 0.0.13 to 0.0.14. +- Bump github.com/gdamore/tcell/v2 from 2.5.1 to 2.5.3. +- Bump github.com/gdamore/tcell/v2 from 2.5.1 to 2.5.2 + +### Removed + +- Removed the `Sourcegraph` badge from the main page. + ## [0.17.0] - 07-Jul-2022 ### Added diff --git a/README.md b/README.md index e38b3f6..76ce74f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ [![Doc Status](https://godoc.org/github.com/mum4k/termdash?status.png)](https://godoc.org/github.com/mum4k/termdash) [![Build Status](https://travis-ci.com/mum4k/termdash.svg?branch=master)](https://travis-ci.com/mum4k/termdash) -[![Sourcegraph](https://sourcegraph.com/github.com/mum4k/termdash/-/badge.svg)](https://sourcegraph.com/github.com/mum4k/termdash?badge) [![Coverage Status](https://coveralls.io/repos/github/mum4k/termdash/badge.svg?branch=master)](https://coveralls.io/github/mum4k/termdash?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/mum4k/termdash)](https://goreportcard.com/report/github.com/mum4k/termdash) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/mum4k/termdash/blob/master/LICENSE) From a9e894f4c1cb0e5b23a849959d4ecbd67acbf5c9 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 13:08:25 -0500 Subject: [PATCH 11/25] Temporarily disable older golang builds and gofmt step. Signed-off-by: Jakub Sobon --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ae633c5..d65556f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - 1.16.x - - 1.17.x + #- 1.16.x + #- 1.17.x - stable before_install: - go install golang.org/x/tools/cmd/cover@latest @@ -15,7 +15,7 @@ script: - go test -v -covermode=count -coverprofile=coverage.out -mod=mod ./... - CGO_ENABLED=1 go test -mod=mod -race ./... - go vet ./... - - diff -u <(echo -n) <(gofmt -d -s .) + #- diff -u <(echo -n) <(gofmt -d -s .) - diff -u <(echo -n) <(./private/scripts/autogen_licences.sh .) - diff -u <(echo -n) <(golint ./...) env: From 94d07aea18a9e95a73fc166246602db8143d5d92 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 13:15:27 -0500 Subject: [PATCH 12/25] Format files with gofmt from Golang 1.20. Signed-off-by: Jakub Sobon --- container/grid/grid.go | 35 ++++++++++++------- private/canvas/braille/braille.go | 14 ++++---- private/draw/vertical_text.go | 11 +++--- private/numbers/trig/trig.go | 17 +++++---- private/segdisp/dotseg/dotseg.go | 26 +++++++------- private/segdisp/segment/segment.go | 13 +++---- private/segdisp/sixteen/sixteen.go | 30 ++++++++-------- termdashdemo/termdashdemo.go | 12 ++++--- widgets/linechart/internal/axes/scale.go | 7 ++-- .../segmentdisplaydemo/segmentdisplaydemo.go | 6 ++-- widgets/text/text.go | 4 ++- .../textinput/textinputdemo/textinputdemo.go | 6 ++-- 12 files changed, 104 insertions(+), 77 deletions(-) diff --git a/container/grid/grid.go b/container/grid/grid.go index e15fe20..8118a12 100644 --- a/container/grid/grid.go +++ b/container/grid/grid.go @@ -53,11 +53,13 @@ func (b *Builder) Build() ([]container.Option, error) { // validate recursively validates the elements that were added to the builder. // Validates the following per each level of Rows or Columns.: -// The subElements are either exactly one Widget or any number of Rows and -// Columns. -// Each individual width or height is in the range 0 < v < 100. -// The sum of all widths is <= 100. -// The sum of all heights is <= 100. +// +// The subElements are either exactly one Widget or any number of Rows and +// Columns. +// Each individual width or height is in the range 0 < v < 100. +// The sum of all widths is <= 100. +// The sum of all heights is <= 100. +// // Argument fixedSizeParent indicates if any of the parent elements uses fixed // size splitType. func validate(elems []Element, fixedSizeParent bool) error { @@ -182,17 +184,26 @@ func build(elems []Element, parentHeightPerc, parentWidthPerc int) []container.O // E.g. multiple rows would specify that they want the outer split percentage // of 25% each, but we are representing them in a tree of containers so the // inner splits vary: -// ╭─────────╮ +// +// ╭─────────╮ +// // 25% │ 25% │ -// │╭───────╮│ --- +// +// │╭───────╮│ --- +// // 25% ││ 33% ││ -// ││╭─────╮││ +// +// ││╭─────╮││ +// // 25% │││ 50% │││ -// ││├─────┤││ 75% +// +// ││├─────┤││ 75% +// // 25% │││ 50% │││ -// ││╰─────╯││ -// │╰───────╯│ -// ╰─────────╯ --- +// +// ││╰─────╯││ +// │╰───────╯│ +// ╰─────────╯ --- // // Argument outerPerc is the user specified percentage for the split, i.e. the // 25% in the example above. diff --git a/private/canvas/braille/braille.go b/private/canvas/braille/braille.go index 7cd902f..85f5577 100644 --- a/private/canvas/braille/braille.go +++ b/private/canvas/braille/braille.go @@ -27,13 +27,13 @@ right and down. Each cell: - X→ 0 1 Y - ┌───┐ ↓ - │● ●│ 0 - │● ●│ 1 - │● ●│ 2 - │● ●│ 3 - └───┘ + X→ 0 1 Y + ┌───┐ ↓ + │● ●│ 0 + │● ●│ 1 + │● ●│ 2 + │● ●│ 3 + └───┘ When using the braille canvas, the coordinates address the sub-cell points rather then cells themselves. However all points in the cell still share the diff --git a/private/draw/vertical_text.go b/private/draw/vertical_text.go index 44aadc9..7cac908 100644 --- a/private/draw/vertical_text.go +++ b/private/draw/vertical_text.go @@ -75,11 +75,12 @@ func VerticalTextOverrunMode(om OverrunMode) VerticalTextOption { // VerticalText prints the provided text on the canvas starting at the provided point. // The text is printed in a vertical orientation, i.e: -// H -// e -// l -// l -// o +// +// H +// e +// l +// l +// o func VerticalText(c *canvas.Canvas, text string, start image.Point, opts ...VerticalTextOption) error { ar := c.Area() if !start.In(ar) { diff --git a/private/numbers/trig/trig.go b/private/numbers/trig/trig.go index 16d179d..a3945bb 100644 --- a/private/numbers/trig/trig.go +++ b/private/numbers/trig/trig.go @@ -128,9 +128,10 @@ func (ar *angleRange) contains(angle int) bool { // normalizeRange normalizes the start and end angles in degrees into ranges of // angles. Useful for cases where the 0/360 point falls within the range. // E.g: -// 0,25 => angleRange{0, 26} -// 0,360 => angleRange{0, 361} -// 359,20 => angleRange{359, 361}, angleRange{0, 21} +// +// 0,25 => angleRange{0, 26} +// 0,360 => angleRange{0, 361} +// 359,20 => angleRange{359, 361}, angleRange{0, 21} func normalizeRange(start, end int) ([]*angleRange, error) { if start < MinAngle || start > MaxAngle { return nil, fmt.Errorf("invalid start angle:%d, must be in range %d <= start <= %d", start, MinAngle, MaxAngle) @@ -159,8 +160,9 @@ func normalizeRange(start, end int) ([]*angleRange, error) { // RangeSize returns the size of the degree range. // E.g: -// 0,25 => 25 -// 359,1 => 2 +// +// 0,25 => 25 +// 359,1 => 2 func RangeSize(start, end int) (int, error) { ranges, err := normalizeRange(start, end) if err != nil { @@ -174,8 +176,9 @@ func RangeSize(start, end int) (int, error) { // RangeMid returns an angle that lies in the middle between start and end. // E.g: -// 0,10 => 5 -// 350,10 => 0 +// +// 0,10 => 5 +// 350,10 => 0 func RangeMid(start, end int) (int, error) { ranges, err := normalizeRange(start, end) if err != nil { diff --git a/private/segdisp/dotseg/dotseg.go b/private/segdisp/dotseg/dotseg.go index 2d603e2..869d177 100644 --- a/private/segdisp/dotseg/dotseg.go +++ b/private/segdisp/dotseg/dotseg.go @@ -21,19 +21,19 @@ display dot characters. The following outlines segments in the display and their names. - --------------- - | | - | | - | | - | o D1 | - | | - | | - | | - | o D2 | - | | - | | - | o D3 | - --------------- + --------------- + | | + | | + | | + | o D1 | + | | + | | + | | + | o D2 | + | | + | | + | o D3 | + --------------- */ package dotseg diff --git a/private/segdisp/segment/segment.go b/private/segdisp/segment/segment.go index 37e4f1f..098c17a 100644 --- a/private/segdisp/segment/segment.go +++ b/private/segdisp/segment/segment.go @@ -95,14 +95,15 @@ func SkipSlopesLTE(v int) Option { // This only has a visible effect when the horizontal segment has height of two // or the vertical segment has width of two. // Without this option segments with height / width of two look like this: -// - | -// --- || -// | +// - | +// --- || +// | // // With this option: -// --- | -// - || -// | +// +// --- | +// - || +// | func ReverseSlopes() Option { return option(func(opts *options) { opts.reverseSlopes = true diff --git a/private/segdisp/sixteen/sixteen.go b/private/segdisp/sixteen/sixteen.go index 2013f7f..eb71643 100644 --- a/private/segdisp/sixteen/sixteen.go +++ b/private/segdisp/sixteen/sixteen.go @@ -21,21 +21,21 @@ display ASCII characters. The following outlines segments in the display and their names. - A1 A2 - ------- ------- - | \ | / | - | \ | / | - F | H J K | B - | \ | / | - | \ | / | - -G1---- ----G2- - | / | \ | - | / | \ | - E | N M L | C - | / | \ | - | / | \ | - ------- ------- - D1 D2 + A1 A2 + ------- ------- + | \ | / | + | \ | / | + F | H J K | B + | \ | / | + | \ | / | + -G1---- ----G2- + | / | \ | + | / | \ | + E | N M L | C + | / | \ | + | / | \ | + ------- ------- + D1 D2 */ package sixteen diff --git a/termdashdemo/termdashdemo.go b/termdashdemo/termdashdemo.go index 2df4499..b9620e2 100644 --- a/termdashdemo/termdashdemo.go +++ b/termdashdemo/termdashdemo.go @@ -976,8 +976,10 @@ func newLayoutButtons(c *container.Container, w *widgets) (*layoutButtons, error // rotateFloats returns a new slice with inputs rotated by step. // I.e. for a step of one: -// inputs[0] -> inputs[len(inputs)-1] -// inputs[1] -> inputs[0] +// +// inputs[0] -> inputs[len(inputs)-1] +// inputs[1] -> inputs[0] +// // And so on. func rotateFloats(inputs []float64, step int) []float64 { return append(inputs[step:], inputs[:step]...) @@ -985,8 +987,10 @@ func rotateFloats(inputs []float64, step int) []float64 { // rotateRunes returns a new slice with inputs rotated by step. // I.e. for a step of one: -// inputs[0] -> inputs[len(inputs)-1] -// inputs[1] -> inputs[0] +// +// inputs[0] -> inputs[len(inputs)-1] +// inputs[1] -> inputs[0] +// // And so on. func rotateRunes(inputs []rune, step int) []rune { return append(inputs[step:], inputs[:step]...) diff --git a/widgets/linechart/internal/axes/scale.go b/widgets/linechart/internal/axes/scale.go index f193aae..10a194c 100644 --- a/widgets/linechart/internal/axes/scale.go +++ b/widgets/linechart/internal/axes/scale.go @@ -328,9 +328,10 @@ func (xs *XScale) CellLabel(x int) (*Value, error) { // the position. Positions grow up, coordinates grow down. // // Positions Y Coordinates -// 2 | 0 -// 1 | 1 -// 0 | 2 +// +// 2 | 0 +// 1 | 1 +// 0 | 2 func positionToY(pos int, height int) (int, error) { max := height - 1 if min := 0; pos < min || pos > max { diff --git a/widgets/segmentdisplay/segmentdisplaydemo/segmentdisplaydemo.go b/widgets/segmentdisplay/segmentdisplaydemo/segmentdisplaydemo.go index 5d04042..698b67f 100644 --- a/widgets/segmentdisplay/segmentdisplaydemo/segmentdisplaydemo.go +++ b/widgets/segmentdisplay/segmentdisplaydemo/segmentdisplaydemo.go @@ -62,8 +62,10 @@ func clock(ctx context.Context, sd *segmentdisplay.SegmentDisplay) { // rotate returns a new slice with inputs rotated by step. // I.e. for a step of one: -// inputs[0] -> inputs[len(inputs)-1] -// inputs[1] -> inputs[0] +// +// inputs[0] -> inputs[len(inputs)-1] +// inputs[1] -> inputs[0] +// // And so on. func rotate(inputs []rune, step int) []rune { return append(inputs[step:], inputs[:step]...) diff --git a/widgets/text/text.go b/widgets/text/text.go index 0cca121..b60d3c4 100644 --- a/widgets/text/text.go +++ b/widgets/text/text.go @@ -105,7 +105,9 @@ func (t *Text) contentCells() int { // Write writes text for the widget to display. Multiple calls append // additional text. The text contain cannot control characters // (unicode.IsControl) or space character (unicode.IsSpace) other than: -// ' ', '\n' +// +// ' ', '\n' +// // Any newline ('\n') characters are interpreted as newlines when displaying // the text. func (t *Text) Write(text string, wOpts ...WriteOption) error { diff --git a/widgets/textinput/textinputdemo/textinputdemo.go b/widgets/textinput/textinputdemo/textinputdemo.go index d4d468c..6784945 100644 --- a/widgets/textinput/textinputdemo/textinputdemo.go +++ b/widgets/textinput/textinputdemo/textinputdemo.go @@ -34,8 +34,10 @@ import ( // rotate returns a new slice with inputs rotated by step. // I.e. for a step of one: -// inputs[0] -> inputs[len(inputs)-1] -// inputs[1] -> inputs[0] +// +// inputs[0] -> inputs[len(inputs)-1] +// inputs[1] -> inputs[0] +// // And so on. func rotate(inputs []rune, step int) []rune { return append(inputs[step:], inputs[:step]...) From b01ab7c72a6fd14ea93de05b2cb20facba98e086 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 13:18:01 -0500 Subject: [PATCH 13/25] Note formatting of Golang files. Signed-off-by: Jakub Sobon --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1b9026..9f5ecfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump github.com/mattn/go-runewidth from 0.0.13 to 0.0.14. - Bump github.com/gdamore/tcell/v2 from 2.5.1 to 2.5.3. - Bump github.com/gdamore/tcell/v2 from 2.5.1 to 2.5.2 +- Formatted all Go files with `gofmt` from Golang v1.20. ### Removed From 155b42c608d22fad4e6e87b29c04868ee1c49c8e Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 13:20:28 -0500 Subject: [PATCH 14/25] Note Golang version updates. Signed-off-by: Jakub Sobon --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f5ecfe..46f21e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump github.com/mattn/go-runewidth from 0.0.13 to 0.0.14. - Bump github.com/gdamore/tcell/v2 from 2.5.1 to 2.5.3. - Bump github.com/gdamore/tcell/v2 from 2.5.1 to 2.5.2 +- Change the Go version in `go.mod` to 1.20. +- Executed `go mod tidy`. +- CI now executes tests with Golang v1.20 only. - Formatted all Go files with `gofmt` from Golang v1.20. ### Removed From efe737b6ac52e6d794955a8a76dee619033769c8 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 13:33:53 -0500 Subject: [PATCH 15/25] Mark release v0.18.0 in the CHANGELOG. Signed-off-by: Jakub Sobon --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46f21e3..06860d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.18.0] - 08-Feb-2023 + ### Added - The `gauge` widget now supports drawing of a vertical threshold bar. @@ -511,6 +513,7 @@ identifiers shouldn't be used externally. - The Text widget. [unreleased]: https://github.com/mum4k/termdash/compare/v0.17.0...devel +[0.18.0]: https://github.com/mum4k/termdash/compare/v0.17.0...v0.18.0 [0.17.0]: https://github.com/mum4k/termdash/compare/v0.16.1...v0.17.0 [0.16.1]: https://github.com/mum4k/termdash/compare/v0.16.0...v0.16.1 [0.16.0]: https://github.com/mum4k/termdash/compare/v0.15.0...v0.16.0 From b1943d95cf515573dd3cecd93230b7fc68c68120 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 13:38:38 -0500 Subject: [PATCH 16/25] Fix tests in editor_test.go. Signed-off-by: Jakub Sobon --- widgets/textinput/editor_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets/textinput/editor_test.go b/widgets/textinput/editor_test.go index 89e6937..10cac7c 100644 --- a/widgets/textinput/editor_test.go +++ b/widgets/textinput/editor_test.go @@ -1523,7 +1523,7 @@ func TestFieldEditor(t *testing.T) { wantView: "", wantContent: "", wantCurIdx: 0, - wantOnChangeCalls: 4, + wantOnChangeCalls: 3, }, { desc: "doesn't insert runes with rune width of zero", From b00342e91cba0a5634f148fc519d4e46ff0e28e3 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 15:29:10 -0500 Subject: [PATCH 17/25] Fix GoLint issue. Signed-off-by: Jakub Sobon --- widgets/textinput/options.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/widgets/textinput/options.go b/widgets/textinput/options.go index 2ff1103..deb429a 100644 --- a/widgets/textinput/options.go +++ b/widgets/textinput/options.go @@ -270,9 +270,10 @@ func OnSubmit(fn SubmitFn) Option { }) } -// The argument to ChangeFn contains all the text in the field after the change. +// ChangeFn when passed to OnChage will be called with all the text in the text +// input each time it gets modified. // -// The callback function must be thread-safe as the keyboard event that +// This function must be thread-safe as the keyboard event that // triggers the submission comes from a separate goroutine. type ChangeFn func(data string) From 6cf1c26327ee9101ce7e18b424defc0b66938b1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 20:32:23 +0000 Subject: [PATCH 18/25] Bump github.com/gdamore/tcell/v2 from 2.5.1 to 2.5.4 Bumps [github.com/gdamore/tcell/v2](https://github.com/gdamore/tcell) from 2.5.1 to 2.5.4. - [Release notes](https://github.com/gdamore/tcell/releases) - [Changelog](https://github.com/gdamore/tcell/blob/main/CHANGESv2.md) - [Commits](https://github.com/gdamore/tcell/compare/v2.5.1...v2.5.4) --- updated-dependencies: - dependency-name: github.com/gdamore/tcell/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 34 +++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index b096ba5..ebebe29 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/mum4k/termdash go 1.17 require ( - github.com/gdamore/tcell/v2 v2.5.1 + github.com/gdamore/tcell/v2 v2.5.4 github.com/kylelemons/godebug v1.1.0 - github.com/mattn/go-runewidth v0.0.13 + github.com/mattn/go-runewidth v0.0.14 github.com/nsf/termbox-go v1.1.1 ) @@ -13,7 +13,7 @@ require ( github.com/gdamore/encoding v1.0.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect - golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e // indirect + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/text v0.5.0 // indirect ) diff --git a/go.sum b/go.sum index a0739c3..6420483 100644 --- a/go.sum +++ b/go.sum @@ -1,27 +1,43 @@ github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.5.1 h1:zc3LPdpK184lBW7syF2a5C6MV827KmErk9jGVnmsl/I= -github.com/gdamore/tcell/v2 v2.5.1/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo= +github.com/gdamore/tcell/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/k= +github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY= github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e h1:CsOuNlbOuf0mzxJIefr6Q4uAUetRUwZE4qt7VfzP+xo= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 0cf808465f0e048f02655710ce5753e3ac3b4dae Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 16:03:09 -0500 Subject: [PATCH 19/25] Updating go.mod. Signed-off-by: Jakub Sobon --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ebebe29..5fcc03e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/mum4k/termdash -go 1.17 +go 1.20 require ( github.com/gdamore/tcell/v2 v2.5.4 From 4627a7620fe97ec823f088727fedf27f8e6ed7d6 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 16:17:15 -0500 Subject: [PATCH 20/25] Updating .travis.yml. - reenable coverage. - remove mod=mod. - reenable gofmt. Signed-off-by: Jakub Sobon --- .travis.yml | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index d65556f..a6cdb10 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,20 @@ language: go go: - #- 1.16.x - #- 1.17.x - - stable + - stable before_install: - - go install golang.org/x/tools/cmd/cover@latest - - go install github.com/mattn/goveralls@latest - - go install golang.org/x/lint/golint@latest + - go install golang.org/x/tools/cmd/cover@latest + - go install github.com/mattn/goveralls@latest + - go install golang.org/x/lint/golint@latest script: - - go get -t ./... - # Temporarily set -mod=mod to allow modification of go.mod and go.sum. - # This seems to be caused by a sum missing in the tcell dependency and - # should be removed when no longer needed. - - go test -v -covermode=count -coverprofile=coverage.out -mod=mod ./... - - CGO_ENABLED=1 go test -mod=mod -race ./... - - go vet ./... - #- diff -u <(echo -n) <(gofmt -d -s .) - - diff -u <(echo -n) <(./private/scripts/autogen_licences.sh .) - - diff -u <(echo -n) <(golint ./...) + - go get -t ./... + - go test -v -covermode=count -coverprofile=coverage.out -mod=mod ./... + - CGO_ENABLED=1 go test -race ./... + - go vet ./... + - diff -u <(echo -n) <(gofmt -d -s .) + - diff -u <(echo -n) <(./private/scripts/autogen_licences.sh .) + - diff -u <(echo -n) <(golint ./...) + - $GOPATH/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN env: global: - - CGO_ENABLED=0 + - CGO_ENABLED=0 + - secure: FEFt9o7MvSvK2lnXJTydml/DCmNqPKIc+YJjPlKhfeGGJyruFvP55qkB0rHN7wb2r4L44qMqtM12c41YzA0sFhXXNowhCB0wd7mfyEt3cCLr4akqRedxOpz+qFBGv2oCBObP6e6irtsN0071KtDz9wsITchme3gf0A8l9PhjPeEu9UVbmN8lipouvQFqmdxqLNyxJ+ykbACkSTwIx2rkvU79aOPDoSnDO56Wqaq7V75CmZ13EtHupgmJIz2GECyIL6Ll6824AQiK1O5XXpnc8973Vw1s6kZB5Tyzvb2KaW5U9KhovgX1mTvnj1XJ//SQvh9yMjC5L51DLNbLSshVo7L4CiJZkq8QQLFILPOMaC1emrmhXxwizjbTmNOo96t2MYk2WASq636Uoez0ZkkZPjuwuKoexFi7r04vHEOjnF2N9liN+M8gwvJ5N/MakbS3ZPbZ+57w4xgwuxJRSUYQoT1VSU8E2rc+DQYa4HzY4wrcrJQG7HBjd4KBHnmHjEDAmawBb6iEI9d5d40j0LThxAOweh05YoWVmnTzMScuO27V5embStXab31jUrD4Qnj5/lliklGdYD5aWLKRZhSeg5mLikUITbhriABuUxMaR1FuQW4r1nSg3n8XA6MbwdvngO2DJyzp5iUQX3yfnUgrGzfp9okrA4CdxUHrY/jj8cw= From 5e6de2ede618c469ab5a727b386108293d34ed8f Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 16:20:33 -0500 Subject: [PATCH 21/25] Remove mod=mod from one more location. Signed-off-by: Jakub Sobon --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a6cdb10..c0bfb93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ before_install: - go install golang.org/x/lint/golint@latest script: - go get -t ./... - - go test -v -covermode=count -coverprofile=coverage.out -mod=mod ./... + - go test -v -covermode=count -coverprofile=coverage.out ./... - CGO_ENABLED=1 go test -race ./... - go vet ./... - diff -u <(echo -n) <(gofmt -d -s .) From bc92fea523224f21d96f59ac257e517c51903771 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 16:22:46 -0500 Subject: [PATCH 22/25] Include Golang v1.20.x explicitly. Signed-off-by: Jakub Sobon --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c0bfb93..96ec6a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: go go: + - 1.20.x - stable before_install: - go install golang.org/x/tools/cmd/cover@latest From 5a36b82b2ed44c300d42fd2df21512a7917e05ec Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 16:30:51 -0500 Subject: [PATCH 23/25] Updating CHANGELOG. Signed-off-by: Jakub Sobon --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06860d5..d13f911 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,12 +24,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Change the Go version in `go.mod` to 1.20. - Executed `go mod tidy`. - CI now executes tests with Golang v1.20 only. -- Formatted all Go files with `gofmt` from Golang v1.20. ### Removed - Removed the `Sourcegraph` badge from the main page. +### Fixed + +- Formatted all Go files with `gofmt` from Golang v1.20. +- Fixed line coverage reporting. + ## [0.17.0] - 07-Jul-2022 ### Added From fa5cbadc3501e0d108152ddd72731fe7aab22fc1 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 16:37:38 -0500 Subject: [PATCH 24/25] Fixing one misformatted doc comment. Signed-off-by: Jakub Sobon --- private/segdisp/segment/segment.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/private/segdisp/segment/segment.go b/private/segdisp/segment/segment.go index 098c17a..78a9a88 100644 --- a/private/segdisp/segment/segment.go +++ b/private/segdisp/segment/segment.go @@ -95,15 +95,15 @@ func SkipSlopesLTE(v int) Option { // This only has a visible effect when the horizontal segment has height of two // or the vertical segment has width of two. // Without this option segments with height / width of two look like this: -// - | +// - | // --- || -// | +// | // // With this option: // -// --- | -// - || -// | +// --- | +// - || +// | func ReverseSlopes() Option { return option(func(opts *options) { opts.reverseSlopes = true From 244687324e4304e2ffa97e8ad542b5587d1ebc63 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 8 Feb 2023 16:48:15 -0500 Subject: [PATCH 25/25] Fix gofmt error. Signed-off-by: Jakub Sobon --- private/segdisp/segment/segment.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/private/segdisp/segment/segment.go b/private/segdisp/segment/segment.go index 78a9a88..49d6c23 100644 --- a/private/segdisp/segment/segment.go +++ b/private/segdisp/segment/segment.go @@ -95,15 +95,15 @@ func SkipSlopesLTE(v int) Option { // This only has a visible effect when the horizontal segment has height of two // or the vertical segment has width of two. // Without this option segments with height / width of two look like this: -// - | -// --- || -// | -// -// With this option: -// -// --- | -// - || -// | +// x - | +// x --- || +// x | +// x +// x With this option: +// x +// x --- | +// x - || +// x | func ReverseSlopes() Option { return option(func(opts *options) { opts.reverseSlopes = true