diff --git a/table.go b/table.go index 265aa9f..fb9e162 100644 --- a/table.go +++ b/table.go @@ -697,7 +697,7 @@ ColumnLoop: expansion := 0 for _, row := range rows { if cell := getCell(row, column); cell != nil { - _, _, _, _, cellWidth := decomposeString(cell.Text) + _, _, _, _, _, _, cellWidth := decomposeString(cell.Text, true, false) if cell.MaxWidth > 0 && cell.MaxWidth < cellWidth { cellWidth = cell.MaxWidth } diff --git a/textview.go b/textview.go index 00ac88c..f0a6499 100644 --- a/textview.go +++ b/textview.go @@ -558,33 +558,11 @@ func (t *TextView) reindexBuffer(width int) { // Go through each line in the buffer. for bufferIndex, str := range t.buffer { - // Find all color tags in this line. Then remove them. - var ( - colorTagIndices [][]int - colorTags [][]string - escapeIndices [][]int - ) - strippedStr := str - if t.dynamicColors { - colorTagIndices, colorTags, escapeIndices, strippedStr, _ = decomposeString(str) - } - - // Find all regions in this line. Then remove them. - var ( - regionIndices [][]int - regions [][]string - ) - if t.regions { - regionIndices = regionPattern.FindAllStringIndex(str, -1) - regions = regionPattern.FindAllStringSubmatch(str, -1) - strippedStr = regionPattern.ReplaceAllString(strippedStr, "") - } - - // We don't need the original string anymore for now. - str = strippedStr + colorTagIndices, colorTags, regionIndices, regions, escapeIndices, strippedStr, _ := decomposeString(str, t.dynamicColors, t.regions) // Split the line if required. var splitLines []string + str = strippedStr if t.wrap && len(str) > 0 { for len(str) > 0 { extract := runewidth.Truncate(str, width, "") @@ -829,31 +807,8 @@ func (t *TextView) Draw(screen tcell.Screen) { attributes := index.Attributes regionID := index.Region - // Get color tags. - var ( - colorTagIndices [][]int - colorTags [][]string - escapeIndices [][]int - ) - strippedText := text - if t.dynamicColors { - colorTagIndices, colorTags, escapeIndices, strippedText, _ = decomposeString(text) - } - - // Get regions. - var ( - regionIndices [][]int - regions [][]string - ) - if t.regions { - regionIndices = regionPattern.FindAllStringIndex(text, -1) - regions = regionPattern.FindAllStringSubmatch(text, -1) - strippedText = regionPattern.ReplaceAllString(strippedText, "") - if !t.dynamicColors { - escapeIndices = escapePattern.FindAllStringIndex(text, -1) - strippedText = string(escapePattern.ReplaceAllString(strippedText, "[$1$2]")) - } - } + // Process tags. + colorTagIndices, colorTags, regionIndices, regions, escapeIndices, strippedText, _ := decomposeString(text, t.dynamicColors, t.regions) // Calculate the position of the line. var skip, posX int diff --git a/util.go b/util.go index e12b53c..1c33345 100644 --- a/util.go +++ b/util.go @@ -3,6 +3,7 @@ package tview import ( "math" "regexp" + "sort" "strconv" "unicode" @@ -160,15 +161,28 @@ func overlayStyle(background tcell.Color, defaultStyle tcell.Style, fgColor, bgC } // decomposeString returns information about a string which may contain color -// tags. It returns the indices of the color tags (as returned by +// tags or region tags, depending on which ones are requested to be found. It +// returns the indices of the color tags (as returned by // re.FindAllStringIndex()), the color tags themselves (as returned by -// re.FindAllStringSubmatch()), the indices of an escaped tags, the string -// stripped by any color tags and escaped, and the screen width of the stripped -// string. -func decomposeString(text string) (colorIndices [][]int, colors [][]string, escapeIndices [][]int, stripped string, width int) { - // Get positions of color and escape tags. - colorIndices = colorPattern.FindAllStringIndex(text, -1) - colors = colorPattern.FindAllStringSubmatch(text, -1) +// re.FindAllStringSubmatch()), the indices of region tags and the region tags +// themselves, the indices of an escaped tags (only if at least color tags or +// region tags are requested), the string stripped by any tags and escaped, and +// the screen width of the stripped string. +func decomposeString(text string, findColors, findRegions bool) (colorIndices [][]int, colors [][]string, regionIndices [][]int, regions [][]string, escapeIndices [][]int, stripped string, width int) { + // Shortcut for the trivial case. + if !findColors && !findRegions { + return nil, nil, nil, nil, nil, text, runewidth.StringWidth(text) + } + + // Get positions of any tags. + if findColors { + colorIndices = colorPattern.FindAllStringIndex(text, -1) + colors = colorPattern.FindAllStringSubmatch(text, -1) + } + if findRegions { + regionIndices = regionPattern.FindAllStringIndex(text, -1) + regions = regionPattern.FindAllStringSubmatch(text, -1) + } escapeIndices = escapePattern.FindAllStringIndex(text, -1) // Because the color pattern detects empty tags, we need to filter them out. @@ -179,10 +193,26 @@ func decomposeString(text string) (colorIndices [][]int, colors [][]string, esca } } - // Remove the color tags from the original string. + // Make a (sorted) list of all tags. + var allIndices [][]int + if findColors && findRegions { + allIndices = colorIndices + allIndices = make([][]int, len(colorIndices)+len(regionIndices)) + copy(allIndices, colorIndices) + copy(allIndices[len(colorIndices):], regionIndices) + sort.Slice(allIndices, func(i int, j int) bool { + return allIndices[i][0] < allIndices[j][0] + }) + } else if findColors { + allIndices = colorIndices + } else { + allIndices = regionIndices + } + + // Remove the tags from the original string. var from int buf := make([]byte, 0, len(text)) - for _, indices := range colorIndices { + for _, indices := range allIndices { buf = append(buf, []byte(text[from:indices[0]])...) from = indices[1] } @@ -218,7 +248,7 @@ func printWithStyle(screen tcell.Screen, text string, x, y, maxWidth, align int, } // Decompose the text. - colorIndices, colors, escapeIndices, strippedText, strippedWidth := decomposeString(text) + colorIndices, colors, _, _, escapeIndices, strippedText, strippedWidth := decomposeString(text, true, false) // We want to reduce all alignments to AlignLeft. if align == AlignRight { @@ -382,7 +412,7 @@ func PrintSimple(screen tcell.Screen, text string, x, y int) { // StringWidth returns the width of the given string needed to print it on // screen. The text may contain color tags which are not counted. func StringWidth(text string) int { - _, _, _, _, width := decomposeString(text) + _, _, _, _, _, _, width := decomposeString(text, true, false) return width } @@ -394,7 +424,7 @@ func StringWidth(text string) int { // // Text is always split at newline characters ('\n'). func WordWrap(text string, width int) (lines []string) { - colorTagIndices, _, escapeIndices, strippedText, _ := decomposeString(text) + colorTagIndices, _, _, _, escapeIndices, strippedText, _ := decomposeString(text, true, false) // Find candidate breakpoints. breakpoints := boundaryPattern.FindAllStringSubmatchIndex(strippedText, -1)