diff --git a/go.mod b/go.mod index 0961830..2f16338 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/rivo/tview go 1.12 require ( - github.com/gdamore/tcell v1.4.0 // indirect github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591 github.com/lucasb-eyer/go-colorful v1.0.3 github.com/mattn/go-runewidth v0.0.10 diff --git a/go.sum b/go.sum index b329b61..a30c2cf 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,10 @@ 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 v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU= -github.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0= -github.com/gdamore/tcell/v2 v2.0.0 h1:GRWG8aLfWAlekj9Q6W29bVvkHENc6hp79XOqG4AWDOs= -github.com/gdamore/tcell/v2 v2.0.0/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591 h1:0WWUDZ1oxq7NxVyGo8M3KI5jbkiwNAdZFFzAdC68up4= github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= @@ -19,14 +13,10 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10= golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7 h1:XtNJkfEjb4zR3q20BBBcYUykVOEMgZeIUOpBPfNYgxg= -golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY= golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/textview.go b/textview.go index de08642..858f253 100644 --- a/textview.go +++ b/textview.go @@ -23,12 +23,11 @@ var ( TabSize = 4 ) -// textViewIndex contains information about each line displayed in the text -// view. +// textViewIndex contains information about a line displayed in the text view. type textViewIndex struct { - Line int // The index into the "buffer" variable. + Line int // The index into the "buffer" slice. Pos int // The index into the "buffer" string (byte position). - NextPos int // The (byte) index of the next character in this buffer line. + NextPos int // The (byte) index of the next line start within this buffer string. Width int // The screen width of this line. ForegroundColor string // The starting foreground color ("" = don't change, "-" = reset). BackgroundColor string // The starting background color ("" = don't change, "-" = reset). @@ -131,7 +130,7 @@ type TextView struct { // A set of region IDs that are currently highlighted. highlights map[string]struct{} - // The last width for which the current table is drawn. + // The last width for which the current text view is drawn. lastWidth int // The screen width of the longest line in the index (not the buffer). @@ -146,6 +145,10 @@ type TextView struct { // The number of characters to be skipped on each line (not in wrap mode). columnOffset int + // The maximum number of lines kept in the line index, effectively the + // latest word-wrapped lines. Ignored if 0. + maxLines int + // The height of the content the last time the text view was drawn. pageSize int @@ -243,6 +246,20 @@ func (t *TextView) SetWordWrap(wrapOnWords bool) *TextView { return t } +// SetMaxLines sets the maximum number of lines for this text view. Lines at the +// beginning of the text will be discarded when the text view is drawn, so as to +// remain below this value. Broken lines via word wrapping are counted +// individually. +// +// Note that GetText() will return the shortened text and may start with color +// and/or region tags that were open at the cutoff point. +// +// A value of 0 (the default) will keep all lines in place. +func (t *TextView) SetMaxLines(maxLines int) *TextView { + t.maxLines = maxLines + return t +} + // SetTextAlign sets the text alignment within the text view. This must be // either AlignLeft, AlignCenter, or AlignRight. func (t *TextView) SetTextAlign(align int) *TextView { @@ -671,7 +688,10 @@ func (t *TextView) Write(p []byte) (n int, err error) { // reindexBuffer re-indexes the buffer such that we can use it to easily draw // the buffer onto the screen. Each line in the index will contain a pointer // into the buffer from which on we will print text. It will also contain the -// color with which the line starts. +// colors, attributes, and region with which the line starts. +// +// If maxLines is greater than 0, any extra lines will be dropped from the +// buffer. func (t *TextView) reindexBuffer(width int) { if t.index != nil { return // Nothing has changed. We can still use the current index. @@ -834,6 +854,56 @@ func (t *TextView) reindexBuffer(width int) { } } + // Drop lines beyond maxLines. + if t.maxLines > 0 && len(t.index) > t.maxLines { + removedLines := len(t.index) - t.maxLines + + // Adjust the index. + t.index = t.index[removedLines:] + if t.fromHighlight >= 0 { + t.fromHighlight -= removedLines + if t.fromHighlight < 0 { + t.fromHighlight = 0 + } + } + if t.toHighlight >= 0 { + t.toHighlight -= removedLines + if t.toHighlight < 0 { + t.fromHighlight, t.toHighlight, t.posHighlight = -1, -1, -1 + } + } + bufferShift := t.index[0].Line + for _, line := range t.index { + line.Line -= bufferShift + } + + // Adjust the original buffer. + t.buffer = t.buffer[bufferShift:] + var prefix string + if t.index[0].ForegroundColor != "" || t.index[0].BackgroundColor != "" || t.index[0].Attributes != "" { + prefix = fmt.Sprintf("[%s:%s:%s]", t.index[0].ForegroundColor, t.index[0].BackgroundColor, t.index[0].Attributes) + } + if t.index[0].Region != "" { + prefix += fmt.Sprintf("[%s]", t.index[0].Region) + } + posShift := t.index[0].Pos + t.buffer[0] = prefix + t.buffer[0][posShift:] + t.lineOffset -= removedLines + if t.lineOffset < 0 { + t.lineOffset = 0 + } + + // Adjust positions of first buffer line. + posShift -= len(prefix) + for _, line := range t.index { + if line.Line != 0 { + break + } + line.Pos -= posShift + line.NextPos -= posShift + } + } + // Calculate longest line. t.longestLine = 0 for _, line := range t.index {