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}