okami.mom/ultraviolet

git clone https://git.nkp.pet/okami.mom/ultraviolet

Ayman Bagabasrefactor: update package name to uvb3a6f4b

main
3.0 KiB142 linesraw
1package uv
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// Width returns the width of the screen that the tab stops are set for.
48func (ts *TabStops) Width() int {
49	return ts.width
50}
51
52// IsStop returns true if the given column is a tab stop.
53func (ts TabStops) IsStop(col int) bool {
54	mask := ts.mask(col)
55	i := col >> 3
56	if i < 0 || i >= len(ts.stops) {
57		return false
58	}
59	return ts.stops[i]&mask != 0
60}
61
62// Next returns the next tab stop after the given column.
63func (ts TabStops) Next(col int) int {
64	return ts.Find(col, 1)
65}
66
67// Prev returns the previous tab stop before the given column.
68func (ts TabStops) Prev(col int) int {
69	return ts.Find(col, -1)
70}
71
72// Find returns the prev/next tab stop before/after the given column and delta.
73// If delta is positive, it returns the next tab stop after the given column.
74// If delta is negative, it returns the previous tab stop before the given column.
75// If delta is zero, it returns the given column.
76func (ts TabStops) Find(col, delta int) int {
77	if delta == 0 {
78		return col
79	}
80
81	var prev bool
82	count := delta
83	if count < 0 {
84		count = -count
85		prev = true
86	}
87
88	for count > 0 {
89		if !prev {
90			if col >= ts.width-1 {
91				return col
92			}
93
94			col++
95		} else {
96			if col < 1 {
97				return col
98			}
99
100			col--
101		}
102
103		if ts.IsStop(col) {
104			count--
105		}
106	}
107
108	return col
109}
110
111// Set adds a tab stop at the given column.
112func (ts *TabStops) Set(col int) {
113	mask := ts.mask(col)
114	ts.stops[col>>3] |= mask
115}
116
117// Reset removes the tab stop at the given column.
118func (ts *TabStops) Reset(col int) {
119	mask := ts.mask(col)
120	ts.stops[col>>3] &= ^mask
121}
122
123// Clear removes all tab stops.
124func (ts *TabStops) Clear() {
125	ts.stops = make([]int, len(ts.stops))
126}
127
128// mask returns the mask for the given column.
129func (ts *TabStops) mask(col int) int {
130	return 1 << (col & (ts.interval - 1))
131}
132
133// init initializes the tab stops starting from col until width.
134func (ts *TabStops) init(col, width int) {
135	for x := col; x < width; x++ {
136		if x%ts.interval == 0 {
137			ts.Set(x)
138		} else {
139			ts.Reset(x)
140		}
141	}
142}