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}