tabstop.go

  1package cellbuf
  2
  3// DefaultTabInterval is the default tab interval.
  4const DefaultTabInterval = 8
  5
  6// TabStops represents horizontal line tab stops.
  7type TabStops struct {
  8	stops    []int
  9	interval int
 10	width    int
 11}
 12
 13// NewTabStops creates a new set of tab stops from a number of columns and an
 14// interval.
 15func NewTabStops(width, interval int) *TabStops {
 16	ts := new(TabStops)
 17	ts.interval = interval
 18	ts.width = width
 19	ts.stops = make([]int, (width+(interval-1))/interval)
 20	ts.init(0, width)
 21	return ts
 22}
 23
 24// DefaultTabStops creates a new set of tab stops with the default interval.
 25func DefaultTabStops(cols int) *TabStops {
 26	return NewTabStops(cols, DefaultTabInterval)
 27}
 28
 29// Resize resizes the tab stops to the given width.
 30func (ts *TabStops) Resize(width int) {
 31	if width == ts.width {
 32		return
 33	}
 34
 35	if width < ts.width {
 36		size := (width + (ts.interval - 1)) / ts.interval
 37		ts.stops = ts.stops[:size]
 38	} else {
 39		size := (width - ts.width + (ts.interval - 1)) / ts.interval
 40		ts.stops = append(ts.stops, make([]int, size)...)
 41	}
 42
 43	ts.init(ts.width, width)
 44	ts.width = width
 45}
 46
 47// IsStop returns true if the given column is a tab stop.
 48func (ts TabStops) IsStop(col int) bool {
 49	mask := ts.mask(col)
 50	i := col >> 3
 51	if i < 0 || i >= len(ts.stops) {
 52		return false
 53	}
 54	return ts.stops[i]&mask != 0
 55}
 56
 57// Next returns the next tab stop after the given column.
 58func (ts TabStops) Next(col int) int {
 59	return ts.Find(col, 1)
 60}
 61
 62// Prev returns the previous tab stop before the given column.
 63func (ts TabStops) Prev(col int) int {
 64	return ts.Find(col, -1)
 65}
 66
 67// Find returns the prev/next tab stop before/after the given column and delta.
 68// If delta is positive, it returns the next tab stop after the given column.
 69// If delta is negative, it returns the previous tab stop before the given column.
 70// If delta is zero, it returns the given column.
 71func (ts TabStops) Find(col, delta int) int {
 72	if delta == 0 {
 73		return col
 74	}
 75
 76	var prev bool
 77	count := delta
 78	if count < 0 {
 79		count = -count
 80		prev = true
 81	}
 82
 83	for count > 0 {
 84		if !prev {
 85			if col >= ts.width-1 {
 86				return col
 87			}
 88
 89			col++
 90		} else {
 91			if col < 1 {
 92				return col
 93			}
 94
 95			col--
 96		}
 97
 98		if ts.IsStop(col) {
 99			count--
100		}
101	}
102
103	return col
104}
105
106// Set adds a tab stop at the given column.
107func (ts *TabStops) Set(col int) {
108	mask := ts.mask(col)
109	ts.stops[col>>3] |= mask
110}
111
112// Reset removes the tab stop at the given column.
113func (ts *TabStops) Reset(col int) {
114	mask := ts.mask(col)
115	ts.stops[col>>3] &= ^mask
116}
117
118// Clear removes all tab stops.
119func (ts *TabStops) Clear() {
120	ts.stops = make([]int, len(ts.stops))
121}
122
123// mask returns the mask for the given column.
124func (ts *TabStops) mask(col int) int {
125	return 1 << (col & (ts.interval - 1))
126}
127
128// init initializes the tab stops starting from col until width.
129func (ts *TabStops) init(col, width int) {
130	for x := col; x < width; x++ {
131		if x%ts.interval == 0 {
132			ts.Set(x)
133		} else {
134			ts.Reset(x)
135		}
136	}
137}