segment.go

  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}