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

Code before:
	defer la.lock.Unlock()
	x, y := runeToByteIndex(pos.X, la.lines[pos.Y].data), pos.Y
	for i := 0; i < len(value); i++ {
		if value[i] == '\n' || (value[i] == '\r' && i < len(value)-1 && value[i+1] == '\n') {
			la.split(Loc{x, y})
			x = 0
			y++

			if value[i] == '\r' {
				i++
			}

			continue
		}
		la.insertByte(Loc{x, y}, value[i])
		x++
	}
}

// InsertByte inserts a byte at a given location
func (la *LineArray) insertByte(pos Loc, value byte) {
	la.lines[pos.Y].data = append(la.lines[pos.Y].data, 0)
	copy(la.lines[pos.Y].data[pos.X+1:], la.lines[pos.Y].data[pos.X:])
	la.lines[pos.Y].data[pos.X] = value
}

// joinLines joins the two lines a and b
func (la *LineArray) joinLines(a, b int) {
	la.lines[a].data = append(la.lines[a].data, la.lines[b].data...)
	la.deleteLine(b)
}

// split splits a line at a given position
func (la *LineArray) split(pos Loc) {
	la.newlineBelow(pos.Y)
	la.lines[pos.Y+1].data = append(la.lines[pos.Y+1].data, la.lines[pos.Y].data[pos.X:]...)
	la.lines[pos.Y+1].state = la.lines[pos.Y].state
	la.lines[pos.Y].state = nil
	la.lines[pos.Y].match = nil
	la.lines[pos.Y+1].match = nil
	la.lines[pos.Y].rehighlight = true
	la.deleteToEnd(Loc{pos.X, pos.Y})
}

// removes from start to end
func (la *LineArray) remove(start, end Loc) []byte {
	la.lock.Lock()
	defer la.lock.Unlock()
	sub := la.substr(start, end)
	startX := runeToByteIndex(start.X, la.lines[start.Y].data)
	endX := runeToByteIndex(end.X, la.lines[end.Y].data)

Review comment (bug):
I would do this (and line 245) in a separate commit, with an explanation in the commit message that this removes unneeded recursion and thus allows locking without deadlocks as a preparation for bla-bla-bla, for the descendants who will dig through the git history and wonder what was this for.

Code after:
		if value[i] == '\n' || (value[i] == '\r' && i < len(value)-1 && value[i+1] == '\n') {
			la.split(Loc{x, y})
			x = 0
			y++

			if value[i] == '\r' {
				i++
			}

			continue
		}
		la.insertByte(Loc{x, y}, value[i])
		x++
	}
}

// InsertByte inserts a byte at a given location
func (la *LineArray) insertByte(pos Loc, value byte) {
	la.lines[pos.Y].data = append(la.lines[pos.Y].data, 0)
	copy(la.lines[pos.Y].data[pos.X+1:], la.lines[pos.Y].data[pos.X:])
	la.lines[pos.Y].data[pos.X] = value
}

// joinLines joins the two lines a and b
func (la *LineArray) joinLines(a, b int) {
	la.lines[a].data = append(la.lines[a].data, la.lines[b].data...)
	la.deleteLine(b)
}

// split splits a line at a given position
func (la *LineArray) split(pos Loc) {
	la.newlineBelow(pos.Y)
	la.lines[pos.Y+1].data = append(la.lines[pos.Y+1].data, la.lines[pos.Y].data[pos.X:]...)
	la.lines[pos.Y+1].state = la.lines[pos.Y].state
	la.lines[pos.Y].state = nil
	la.lines[pos.Y].match = nil
	la.lines[pos.Y+1].match = nil
	la.deleteToEnd(Loc{pos.X, pos.Y})
}

// removes from start to end
func (la *LineArray) remove(start, end Loc) []byte {
	la.lock.Lock()
	defer la.lock.Unlock()

	sub := la.Substr(start, end)
	startX := runeToByteIndex(start.X, la.lines[start.Y].data)
	endX := runeToByteIndex(end.X, la.lines[end.Y].data)
	if start.Y == end.Y {
		la.lines[start.Y].data = append(la.lines[start.Y].data[:startX], la.lines[start.Y].data[endX:]...)
	} else {