diff --git a/ui/buffer/buffer.go b/ui/buffer/buffer.go index b253c3c..9e1c48b 100644 --- a/ui/buffer/buffer.go +++ b/ui/buffer/buffer.go @@ -62,6 +62,14 @@ type Buffer interface { // the last rune before the line delimiter. ClampLineCol(line, col int) (int, int) + + // LineColToPos returns the index of the byte at line, col. If line is less than + // zero, or more than the number of available lines, the function will panic. If + // col is less than zero, the function will panic. If col is greater than the + // length of the line, the position of the last byte of the line is returned, + // instead. + LineColToPos(line, col int) int + // PosToLineCol converts a byte offset (position) of the buffer's bytes, into // a line and column. Unless you are working with the Bytes() function, this // is unlikely to be useful to you. Position will be clamped. diff --git a/ui/buffer/highlighter.go b/ui/buffer/highlighter.go index bee9997..91da6c0 100755 --- a/ui/buffer/highlighter.go +++ b/ui/buffer/highlighter.go @@ -81,21 +81,29 @@ func (h *Highlighter) UpdateLines(startLine, endLine int) { } // If the rule k does not have an End, then it can be optimized that we search from the start - // of view until the end of view. For any k that has an End, we search for starts from start - // of buffer, until end of view. + // of view until the end of view. For any k that has an End, we search for ends from start + // of view, backtracking when one is found, to fulfill a multiline highlight. endLine, endCol := h.Buffer.ClampLineCol(endLine, (h.Buffer).RunesInLineWithDelim(endLine)-1) - bytes := (h.Buffer).Slice(0, 0, endLine, endCol) // Allocates size of the buffer + startPos := h.Buffer.LineColToPos(startLine, 0) + bytes := h.Buffer.Slice(startLine, 0, endLine, endCol) for k, v := range h.Language.Rules { - indexes := k.Start.FindAllIndex(bytes, -1) // Attempt to find the start match + var indexes [][]int // [][2]int + if k.End != nil && k.End.String() != "$" { // If this range might be a multiline range... + endIndexes := k.End.FindAllIndex(bytes, -1) // Attempt to find every ending match + startIndexes := k.Start.FindAllIndex(bytes, -1) // Attempt to find every starting match + // ... + _ = endIndexes + _ = startIndexes + } else { // A standard single-line match + indexes = k.Start.FindAllIndex(bytes, -1) // Attempt to find the start match + } + if indexes != nil { for i := range indexes { -// if k.End != nil && k.End.String() != "$" { // If this match has a defined end... -// } - endPos := indexes[i][1] - 1 - startLine, startCol := h.Buffer.PosToLineCol(indexes[i][0]) - endLine, endCol := h.Buffer.PosToLineCol(endPos) + startLine, startCol := h.Buffer.PosToLineCol(indexes[i][0] + startPos) + endLine, endCol := h.Buffer.PosToLineCol(indexes[i][1]-1 + startPos) match := Match { startCol, endLine, endCol, v } diff --git a/ui/buffer/rope.go b/ui/buffer/rope.go index c688229..dfd1f97 100644 --- a/ui/buffer/rope.go +++ b/ui/buffer/rope.go @@ -13,11 +13,12 @@ func NewRopeBuffer(contents []byte) *RopeBuffer { return (*RopeBuffer)(rope.New(contents)) } -// Returns the index of the byte at line, col. If line is less than zero, or more -// than the number of available lines, the function will panic. If col is less than -// zero, the function will panic. If col is greater than the length of the line, -// the position of the last byte of the line is returned, instead. -func (b *RopeBuffer) pos(line, col int) int { +// LineColToPos returns the index of the byte at line, col. If line is less than +// zero, or more than the number of available lines, the function will panic. If +// col is less than zero, the function will panic. If col is greater than the +// length of the line, the position of the last byte of the line is returned, +// instead. +func (b *RopeBuffer) LineColToPos(line, col int) int { var pos int _rope := (*rope.Node)(b) @@ -96,11 +97,11 @@ func (b *RopeBuffer) Line(line int) []byte { // inclusive bounds. The returned value may or may not be a copy of the data, // so do not write to it. func (b *RopeBuffer) Slice(startLine, startCol, endLine, endCol int) []byte { - endPos := b.pos(endLine, endCol)+1 + endPos := b.LineColToPos(endLine, endCol)+1 if length := (*rope.Node)(b).Len(); endPos >= length { endPos = length-1 } - return (*rope.Node)(b).Slice(b.pos(startLine, startCol), endPos) + return (*rope.Node)(b).Slice(b.LineColToPos(startLine, startCol), endPos) } // Bytes returns all of the bytes in the buffer. This function is very likely @@ -112,14 +113,14 @@ func (b *RopeBuffer) Bytes() []byte { // Insert copies a byte slice (inserting it) into the position at line, col. func (b *RopeBuffer) Insert(line, col int, value []byte) { - (*rope.Node)(b).Insert(b.pos(line, col), value) + (*rope.Node)(b).Insert(b.LineColToPos(line, col), value) } // Remove deletes any characters between startLine, startCol, and endLine, // endCol, inclusive bounds. func (b *RopeBuffer) Remove(startLine, startCol, endLine, endCol int) { - start := b.pos(startLine, startCol) - end := b.pos(endLine, endCol) + 1 + start := b.LineColToPos(startLine, startCol) + end := b.LineColToPos(endLine, endCol) + 1 if len := (*rope.Node)(b).Len(); end >= len { end = len @@ -134,8 +135,8 @@ func (b *RopeBuffer) Remove(startLine, startCol, endLine, endCol int) { // Returns the number of occurrences of 'sequence' in the buffer, within the range // of start line and col, to end line and col. End is exclusive. func (b *RopeBuffer) Count(startLine, startCol, endLine, endCol int, sequence []byte) int { - startPos := b.pos(startLine, startCol) - endPos := b.pos(endLine, endCol) + startPos := b.LineColToPos(startLine, startCol) + endPos := b.LineColToPos(endLine, endCol) return (*rope.Node)(b).Count(startPos, endPos, sequence) }