github-vet / github-vet/rangeloop-pointer-findings
dhleong/beholder: src/ui/tui/async-textview.go; 162 LoC
- Dominant language
- No language data
- Stars
- 0
- Forks
- 0
- PR merge metrics
- PR metrics pending
Description
Found a possible issue in [dhleong/beholder](https://www.github.com/dhleong/beholder) at [src/ui/tui/async-textview.go](https://github.com/dhleong/beholder/blob/1459c67907c436f6abc2abcd82c817e177fcd85f/src/ui/tui/async-textview.go#L618-L779)
Below is the message reported by the analyzer for this snippet of code. Beware that the analyzer only reports the first issue it finds, so please do not limit your consideration to the contents of the below message.
> range-loop variable bufferIndex used in defer or goroutine at line 774
[Click here to see the code in its original context.](https://github.com/dhleong/beholder/blob/1459c67907c436f6abc2abcd82c817e177fcd85f/src/ui/tui/async-textview.go#L618-L779)
Click here to show the 162 line(s) of Go which triggered the analyzer.
```go
for bufferIndex, str := range t.buffer[fromBufferIndex:] {
// BEGIN dhleong modification for beholder:
bufferIndex += fromBufferIndex
// END
// Find all color tags in this line. Then remove them.
var (
colorTagIndices [][]int
colorTags [][]string
escapeIndices [][]int
)
if t.dynamicColors {
colorTagIndices, colorTags, escapeIndices, str, _ = 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)
str = regionPattern.ReplaceAllString(str, "")
if !t.dynamicColors {
// We haven't detected escape tags yet. Do it now.
escapeIndices = escapePattern.FindAllStringIndex(str, -1)
str = escapePattern.ReplaceAllString(str, "[$1$2]")
}
}
// Split the line if required.
var splitLines []string
if t.wrap && len(str) > 0 {
for len(str) > 0 {
extract := runewidth.Truncate(str, width, "")
if t.wordWrap && len(extract) < len(str) {
// Add any spaces from the next line.
if spaces := spacePattern.FindStringIndex(str[len(extract):]); spaces != nil && spaces[0] == 0 {
extract = str[:len(extract)+spaces[1]]
}
// Can we split before the mandatory end?
matches := boundaryPattern.FindAllStringIndex(extract, -1)
if len(matches) > 0 {
// Yes. Let's split there.
extract = extract[:matches[len(matches)-1][1]]
}
}
splitLines = append(splitLines, extract)
str = str[len(extract):]
}
} else {
// No need to split the line.
splitLines = []string{str}
}
// Create index from split lines.
var (
originalPos, colorPos, regionPos, escapePos int
foregroundColor, backgroundColor, attributes string
)
for _, splitLine := range splitLines {
line := &textViewIndex{
Line: bufferIndex,
Pos: originalPos,
ForegroundColor: foregroundColor,
BackgroundColor: backgroundColor,
Attributes: attributes,
Region: regionID,
}
// Shift original position with tags.
lineLength := len(splitLine)
for {
if colorPos < len(colorTagIndices) && colorTagIndices[colorPos][0] <= originalPos+lineLength {
// Process color tags.
originalPos += colorTagIndices[colorPos][1] - colorTagIndices[colorPos][0]
foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colorTags[colorPos])
colorPos++
} else if regionPos < len(regionIndices) && regionIndices[regionPos][0] <= originalPos+lineLength {
// Process region tags.
originalPos += regionIndices[regionPos][1] - regionIndices[regionPos][0]
regionID = regions[regionPos][1]
_, highlighted = t.highlights[regionID]
// Update highlight range.
if highlighted {
line := len(t.index)
if t.fromHighlight < 0 {
t.fromHighlight, t.toHighlight = line, line
} else if line > t.toHighlight {
t.toHighlight = line
}
}
regionPos++
} else if escapePos < len(escapeIndices) && escapeIndices[escapePos][0] <= originalPos+lineLength {
// Process escape tags.
originalPos++
escapePos++
} else {
break
}
}
// Advance to next line.
originalPos += lineLength
// Append this line.
line.NextPos = originalPos
line.Width = runewidth.StringWidth(splitLine)
t.index = append(t.index, line)
}
// Word-wrapped lines may have trailing whitespace. Remove it.
if t.wrap && t.wordWrap {
// BEGIN dhleong modification for beholder:
t.reindexMutex.Lock()
if myBuffer != t.currentBuffer {
// stop early; we're old news
t.reindexMutex.Unlock()
return
}
// END
for _, line := range t.index {
str := t.buffer[line.Line][line.Pos:line.NextPos]
spaces := spacePattern.FindAllStringIndex(str, -1)
if spaces != nil && spaces[len(spaces)-1][1] == len(str) {
oldNextPos := line.NextPos
line.NextPos -= spaces[len(spaces)-1][1] - spaces[len(spaces)-1][0]
line.Width -= runewidth.StringWidth(t.buffer[line.Line][line.NextPos:oldNextPos])
}
}
// BEGIN dhleong modification for beholder:
t.reindexMutex.Unlock()
// END
}
// BEGIN dhleong modification for beholder:
if fromBufferIndex == 0 && len(t.index) > height {
// NOTE: for super long buffers, the index process
// can take a noticeable amount of time, especially
// for something like the Combat page in the SRD.
// To avoid slowing down searching, we index enough
// to fit on one page, and then finish the rest
// asynchronously. We probably *should* make more
// judicious use of locks....
go func() {
t.Lock()
defer t.Unlock()
t.reindexBuffer(bufferIndex+1, width, height)
}()
break
}
// END
}
```
Leave a reaction on this issue to contribute to the project by classifying this instance as a **Bug** :-1:, **Mitigated** :+1:, or **Desirable Behavior** :rocket:
See the descriptions of the classifications [here](https://github.com/github-vet/rangeclosure-findings#how-can-i-help) for more information.
commit ID: 1459c67907c436f6abc2abcd82c817e177fcd85f
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with src/ui/tui/async-textview.go around lines 618-779, especially the range loop and goroutine near line 774. Trace how bufferIndex is used by the asynchronous reindexBuffer call and compare the analyzer warning with the intended indexing behavior; done means recording whether this is a Bug, Mitigated, or Desirable Behavior as requested.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100