// Package buffer holds the text of one edited file, together with the cursor, // the selection and the undo history. // // It knows nothing about terminals, rendering or syntax colouring: everything // here is plain data manipulation, which makes it fully testable on its own. // Positions are expressed in runes, never in bytes, so that accented letters // and CJK characters count as one column like any other character. package buffer // Position is a place in the text, as a zero-based line number and a // zero-based column counted in runes. // // A column equal to the line length is legal: it is the position just after // the last character, where the cursor sits when you press End. type Position struct { Line int Col int } // Before reports whether p comes strictly earlier in the text than other. func (p Position) Before(other Position) bool { if p.Line != other.Line { return p.Line < other.Line } return p.Col < other.Col } // After reports whether p comes strictly later in the text than other. func (p Position) After(other Position) bool { return other.Before(p) } // Range is a half-open span of text: Start is included, End is excluded. // // A range whose Start equals its End is empty and covers no character; that is // how an insertion point is expressed. type Range struct { Start Position End Position } // NewRange returns the range covering a and b, whichever of the two comes // first in the text. Use it when a range is built from two user-supplied // points, such as a selection anchor and the cursor. func NewRange(a, b Position) Range { if b.Before(a) { return Range{Start: b, End: a} } return Range{Start: a, End: b} } // IsEmpty reports whether the range covers no character at all. func (r Range) IsEmpty() bool { return r.Start == r.End } // Contains reports whether p falls inside the range. The start is included and // the end is excluded, so an empty range contains nothing. func (r Range) Contains(p Position) bool { return !p.Before(r.Start) && p.Before(r.End) }