Repository: zyedidia/micro
File: internal/buffer/buffer.go (Go)

Code before:
// FindMatchingBrace returns the location in the buffer of the matching bracket
// It is given a brace type containing the open and closing character, (for example
// '{' and '}') as well as the location to match from
// TODO: maybe can be more efficient with utf8 package
// returns the location of the matching brace
// if the boolean returned is true then the original matching brace is one character left
// of the starting location
func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) (Loc, bool, bool) {
	curLine := []rune(string(b.LineBytes(start.Y)))
	startChar := ' '
	if start.X >= 0 && start.X < len(curLine) {
		startChar = curLine[start.X]
	}
	leftChar := ' '
	if start.X-1 >= 0 && start.X-1 < len(curLine) {
		leftChar = curLine[start.X-1]
	}
	var i int
	if startChar == braceType[0] || (leftChar == braceType[0] && startChar != braceType[1]) {
		for y := start.Y; y < b.LinesNum(); y++ {
			l := []rune(string(b.LineBytes(y)))
			xInit := 0
			if y == start.Y {
				if startChar == braceType[0] {
					xInit = start.X
				} else {
					xInit = start.X - 1
				}
			}
			for x := xInit; x < len(l); x++ {
				r := l[x]
				if r == braceType[0] {
					i++
				} else if r == braceType[1] {
					i--
					if i == 0 {
						if startChar == braceType[0] {
							return Loc{x, y}, false, true
						}
						return Loc{x, y}, true, true
					}
				}
			}
		}
	} else if startChar == braceType[1] || leftChar == braceType[1] {
		for y := start.Y; y >= 0; y-- {
			l := []rune(string(b.lines[y].data))
			xInit := len(l) - 1
			if y == start.Y {
				if startChar == braceType[1] {
					xInit = start.X

Review comment (bug):
Regarding Co-authored-by in the commit message: this is the only line I wrote, the rest of this patch was written by JoeKar.

BTW if you want to correct the commit message, I believe it requires force-pushing, so it might be also an occasion to clean up the history of patches in this PR (to ease its further review by @zyedidia or others), for example:
- Squash small uninteresting changes like fixing whitespaces and typos
- Squash "Fix cursor color being double-inverted" and "Fix cursor color being double-inverted, again" into one patch
- Remove both "Fix cursor color being double-inverted when braces are next to each other" and "Restore the simple logic in displayBuffer()" (the latter one just reverts the former one, and the former one is rather completely broken anyway)
- In this "Correct FindMatchingBrace() counting" patch, clarify in the commit message that it makes "match brace under the cursor" have priority over "match brace left of the cursor" in ambiguous cases 

Code after:

var BracePairs = [][2]rune{
	{'(', ')'},
	{'{', '}'},
	{'[', ']'},
}

// FindMatchingBrace returns the location in the buffer of the matching bracket
// It is given a brace type containing the open and closing character, (for example
// '{' and '}') as well as the location to match from
// TODO: maybe can be more efficient with utf8 package
// returns the location of the matching brace
// if the boolean returned is true then the original matching brace is one character left
// of the starting location
func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) (Loc, bool, bool) {
	curLine := []rune(string(b.LineBytes(start.Y)))
	startChar := ' '
	if start.X >= 0 && start.X < len(curLine) {
		startChar = curLine[start.X]
	}
	leftChar := ' '
	if start.X-1 >= 0 && start.X-1 < len(curLine) {
		leftChar = curLine[start.X-1]
	}
	var i int
	if startChar == braceType[0] || (leftChar == braceType[0] && startChar != braceType[1]) {
		for y := start.Y; y < b.LinesNum(); y++ {
			l := []rune(string(b.LineBytes(y)))
			xInit := 0
			if y == start.Y {
				if startChar == braceType[0] {
					xInit = start.X
				} else {
					xInit = start.X - 1
				}
			}
			for x := xInit; x < len(l); x++ {
				r := l[x]
				if r == braceType[0] {
					i++
				} else if r == braceType[1] {
					i--
					if i == 0 {
						if startChar == braceType[0] {
							return Loc{x, y}, false, true
						}
						return Loc{x, y}, true, true
					}
				}
			}
		}