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