1package text
2
3import (
4	"bytes"
5	"github.com/yuin/goldmark/util"
6)
7
8var space = []byte(" ")
9
10// A Segment struct holds information about source positions.
11type Segment struct {
12	// Start is a start position of the segment.
13	Start int
14
15	// Stop is a stop position of the segment.
16	// This value should be excluded.
17	Stop int
18
19	// Padding is a padding length of the segment.
20	Padding int
21}
22
23// NewSegment return a new Segment.
24func NewSegment(start, stop int) Segment {
25	return Segment{
26		Start:   start,
27		Stop:    stop,
28		Padding: 0,
29	}
30}
31
32// NewSegmentPadding returns a new Segment with the given padding.
33func NewSegmentPadding(start, stop, n int) Segment {
34	return Segment{
35		Start:   start,
36		Stop:    stop,
37		Padding: n,
38	}
39}
40
41// Value returns a value of the segment.
42func (t *Segment) Value(buffer []byte) []byte {
43	if t.Padding == 0 {
44		return buffer[t.Start:t.Stop]
45	}
46	result := make([]byte, 0, t.Padding+t.Stop-t.Start+1)
47	result = append(result, bytes.Repeat(space, t.Padding)...)
48	return append(result, buffer[t.Start:t.Stop]...)
49}
50
51// Len returns a length of the segment.
52func (t *Segment) Len() int {
53	return t.Stop - t.Start + t.Padding
54}
55
56// Between returns a segment between this segment and the given segment.
57func (t *Segment) Between(other Segment) Segment {
58	if t.Stop != other.Stop {
59		panic("invalid state")
60	}
61	return NewSegmentPadding(
62		t.Start,
63		other.Start,
64		t.Padding-other.Padding,
65	)
66}
67
68// IsEmpty returns true if this segment is empty, otherwise false.
69func (t *Segment) IsEmpty() bool {
70	return t.Start >= t.Stop && t.Padding == 0
71}
72
73// TrimRightSpace returns a new segment by slicing off all trailing
74// space characters.
75func (t *Segment) TrimRightSpace(buffer []byte) Segment {
76	v := buffer[t.Start:t.Stop]
77	l := util.TrimRightSpaceLength(v)
78	if l == len(v) {
79		return NewSegment(t.Start, t.Start)
80	}
81	return NewSegmentPadding(t.Start, t.Stop-l, t.Padding)
82}
83
84// TrimLeftSpace returns a new segment by slicing off all leading
85// space characters including padding.
86func (t *Segment) TrimLeftSpace(buffer []byte) Segment {
87	v := buffer[t.Start:t.Stop]
88	l := util.TrimLeftSpaceLength(v)
89	return NewSegment(t.Start+l, t.Stop)
90}
91
92// TrimLeftSpaceWidth returns a new segment by slicing off leading space
93// characters until the given width.
94func (t *Segment) TrimLeftSpaceWidth(width int, buffer []byte) Segment {
95	padding := t.Padding
96	for ; width > 0; width-- {
97		if padding == 0 {
98			break
99		}
100		padding--
101	}
102	if width == 0 {
103		return NewSegmentPadding(t.Start, t.Stop, padding)
104	}
105	text := buffer[t.Start:t.Stop]
106	start := t.Start
107	for _, c := range text {
108		if start >= t.Stop-1 || width <= 0 {
109			break
110		}
111		if c == ' ' {
112			width--
113		} else if c == '\t' {
114			width -= 4
115		} else {
116			break
117		}
118		start++
119	}
120	if width < 0 {
121		padding = width * -1
122	}
123	return NewSegmentPadding(start, t.Stop, padding)
124}
125
126// WithStart returns a new Segment with same value except Start.
127func (t *Segment) WithStart(v int) Segment {
128	return NewSegmentPadding(v, t.Stop, t.Padding)
129}
130
131// WithStop returns a new Segment with same value except Stop.
132func (t *Segment) WithStop(v int) Segment {
133	return NewSegmentPadding(t.Start, v, t.Padding)
134}
135
136// ConcatPadding concats the padding to the given slice.
137func (t *Segment) ConcatPadding(v []byte) []byte {
138	if t.Padding > 0 {
139		return append(v, bytes.Repeat(space, t.Padding)...)
140	}
141	return v
142}
143
144// Segments is a collection of the Segment.
145type Segments struct {
146	values []Segment
147}
148
149// NewSegments return a new Segments.
150func NewSegments() *Segments {
151	return &Segments{
152		values: nil,
153	}
154}
155
156// Append appends the given segment after the tail of the collection.
157func (s *Segments) Append(t Segment) {
158	if s.values == nil {
159		s.values = make([]Segment, 0, 20)
160	}
161	s.values = append(s.values, t)
162}
163
164// AppendAll appends all elements of given segments after the tail of the collection.
165func (s *Segments) AppendAll(t []Segment) {
166	if s.values == nil {
167		s.values = make([]Segment, 0, 20)
168	}
169	s.values = append(s.values, t...)
170}
171
172// Len returns the length of the collection.
173func (s *Segments) Len() int {
174	if s.values == nil {
175		return 0
176	}
177	return len(s.values)
178}
179
180// At returns a segment at the given index.
181func (s *Segments) At(i int) Segment {
182	return s.values[i]
183}
184
185// Set sets the given Segment.
186func (s *Segments) Set(i int, v Segment) {
187	s.values[i] = v
188}
189
190// SetSliced replace the collection with a subsliced value.
191func (s *Segments) SetSliced(lo, hi int) {
192	s.values = s.values[lo:hi]
193}
194
195// Sliced returns a subslice of the collection.
196func (s *Segments) Sliced(lo, hi int) []Segment {
197	return s.values[lo:hi]
198}
199
200// Clear delete all element of the collection.
201func (s *Segments) Clear() {
202	s.values = nil
203}
204
205// Unshift insert the given Segment to head of the collection.
206func (s *Segments) Unshift(v Segment) {
207	s.values = append(s.values[0:1], s.values[0:]...)
208	s.values[0] = v
209}
210