Fixed region/color tag escaping bug. Fixes #234

This commit is contained in:
Oliver 2019-02-13 16:07:01 +01:00
parent 84fdb36408
commit a45c8edf60
3 changed files with 48 additions and 63 deletions

View File

@ -697,7 +697,7 @@ ColumnLoop:
expansion := 0 expansion := 0
for _, row := range rows { for _, row := range rows {
if cell := getCell(row, column); cell != nil { if cell := getCell(row, column); cell != nil {
_, _, _, _, cellWidth := decomposeString(cell.Text) _, _, _, _, _, _, cellWidth := decomposeString(cell.Text, true, false)
if cell.MaxWidth > 0 && cell.MaxWidth < cellWidth { if cell.MaxWidth > 0 && cell.MaxWidth < cellWidth {
cellWidth = cell.MaxWidth cellWidth = cell.MaxWidth
} }

View File

@ -558,33 +558,11 @@ func (t *TextView) reindexBuffer(width int) {
// Go through each line in the buffer. // Go through each line in the buffer.
for bufferIndex, str := range t.buffer { for bufferIndex, str := range t.buffer {
// Find all color tags in this line. Then remove them. colorTagIndices, colorTags, regionIndices, regions, escapeIndices, strippedStr, _ := decomposeString(str, t.dynamicColors, t.regions)
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
// Split the line if required. // Split the line if required.
var splitLines []string var splitLines []string
str = strippedStr
if t.wrap && len(str) > 0 { if t.wrap && len(str) > 0 {
for len(str) > 0 { for len(str) > 0 {
extract := runewidth.Truncate(str, width, "") extract := runewidth.Truncate(str, width, "")
@ -829,31 +807,8 @@ func (t *TextView) Draw(screen tcell.Screen) {
attributes := index.Attributes attributes := index.Attributes
regionID := index.Region regionID := index.Region
// Get color tags. // Process tags.
var ( colorTagIndices, colorTags, regionIndices, regions, escapeIndices, strippedText, _ := decomposeString(text, t.dynamicColors, t.regions)
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]"))
}
}
// Calculate the position of the line. // Calculate the position of the line.
var skip, posX int var skip, posX int

56
util.go
View File

@ -3,6 +3,7 @@ package tview
import ( import (
"math" "math"
"regexp" "regexp"
"sort"
"strconv" "strconv"
"unicode" "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 // 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.FindAllStringIndex()), the color tags themselves (as returned by
// re.FindAllStringSubmatch()), the indices of an escaped tags, the string // re.FindAllStringSubmatch()), the indices of region tags and the region tags
// stripped by any color tags and escaped, and the screen width of the stripped // themselves, the indices of an escaped tags (only if at least color tags or
// string. // region tags are requested), the string stripped by any tags and escaped, and
func decomposeString(text string) (colorIndices [][]int, colors [][]string, escapeIndices [][]int, stripped string, width int) { // the screen width of the stripped string.
// Get positions of color and escape tags. func decomposeString(text string, findColors, findRegions bool) (colorIndices [][]int, colors [][]string, regionIndices [][]int, regions [][]string, escapeIndices [][]int, stripped string, width int) {
colorIndices = colorPattern.FindAllStringIndex(text, -1) // Shortcut for the trivial case.
colors = colorPattern.FindAllStringSubmatch(text, -1) 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) escapeIndices = escapePattern.FindAllStringIndex(text, -1)
// Because the color pattern detects empty tags, we need to filter them out. // 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 var from int
buf := make([]byte, 0, len(text)) buf := make([]byte, 0, len(text))
for _, indices := range colorIndices { for _, indices := range allIndices {
buf = append(buf, []byte(text[from:indices[0]])...) buf = append(buf, []byte(text[from:indices[0]])...)
from = indices[1] from = indices[1]
} }
@ -218,7 +248,7 @@ func printWithStyle(screen tcell.Screen, text string, x, y, maxWidth, align int,
} }
// Decompose the text. // 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. // We want to reduce all alignments to AlignLeft.
if align == AlignRight { 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 // StringWidth returns the width of the given string needed to print it on
// screen. The text may contain color tags which are not counted. // screen. The text may contain color tags which are not counted.
func StringWidth(text string) int { func StringWidth(text string) int {
_, _, _, _, width := decomposeString(text) _, _, _, _, _, _, width := decomposeString(text, true, false)
return width return width
} }
@ -394,7 +424,7 @@ func StringWidth(text string) int {
// //
// Text is always split at newline characters ('\n'). // Text is always split at newline characters ('\n').
func WordWrap(text string, width int) (lines []string) { func WordWrap(text string, width int) (lines []string) {
colorTagIndices, _, escapeIndices, strippedText, _ := decomposeString(text) colorTagIndices, _, _, _, escapeIndices, strippedText, _ := decomposeString(text, true, false)
// Find candidate breakpoints. // Find candidate breakpoints.
breakpoints := boundaryPattern.FindAllStringSubmatchIndex(strippedText, -1) breakpoints := boundaryPattern.FindAllStringSubmatchIndex(strippedText, -1)