Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
4.7 KiB
209 lines
4.7 KiB
package text |
|
|
|
import ( |
|
"bytes" |
|
"github.com/yuin/goldmark/util" |
|
) |
|
|
|
var space = []byte(" ") |
|
|
|
// A Segment struct holds information about source positions. |
|
type Segment struct { |
|
// Start is a start position of the segment. |
|
Start int |
|
|
|
// Stop is a stop position of the segment. |
|
// This value should be excluded. |
|
Stop int |
|
|
|
// Padding is a padding length of the segment. |
|
Padding int |
|
} |
|
|
|
// NewSegment return a new Segment. |
|
func NewSegment(start, stop int) Segment { |
|
return Segment{ |
|
Start: start, |
|
Stop: stop, |
|
Padding: 0, |
|
} |
|
} |
|
|
|
// NewSegmentPadding returns a new Segment with the given padding. |
|
func NewSegmentPadding(start, stop, n int) Segment { |
|
return Segment{ |
|
Start: start, |
|
Stop: stop, |
|
Padding: n, |
|
} |
|
} |
|
|
|
// Value returns a value of the segment. |
|
func (t *Segment) Value(buffer []byte) []byte { |
|
if t.Padding == 0 { |
|
return buffer[t.Start:t.Stop] |
|
} |
|
result := make([]byte, 0, t.Padding+t.Stop-t.Start+1) |
|
result = append(result, bytes.Repeat(space, t.Padding)...) |
|
return append(result, buffer[t.Start:t.Stop]...) |
|
} |
|
|
|
// Len returns a length of the segment. |
|
func (t *Segment) Len() int { |
|
return t.Stop - t.Start + t.Padding |
|
} |
|
|
|
// Between returns a segment between this segment and the given segment. |
|
func (t *Segment) Between(other Segment) Segment { |
|
if t.Stop != other.Stop { |
|
panic("invalid state") |
|
} |
|
return NewSegmentPadding( |
|
t.Start, |
|
other.Start, |
|
t.Padding-other.Padding, |
|
) |
|
} |
|
|
|
// IsEmpty returns true if this segment is empty, otherwise false. |
|
func (t *Segment) IsEmpty() bool { |
|
return t.Start >= t.Stop && t.Padding == 0 |
|
} |
|
|
|
// TrimRightSpace returns a new segment by slicing off all trailing |
|
// space characters. |
|
func (t *Segment) TrimRightSpace(buffer []byte) Segment { |
|
v := buffer[t.Start:t.Stop] |
|
l := util.TrimRightSpaceLength(v) |
|
if l == len(v) { |
|
return NewSegment(t.Start, t.Start) |
|
} |
|
return NewSegmentPadding(t.Start, t.Stop-l, t.Padding) |
|
} |
|
|
|
// TrimLeftSpace returns a new segment by slicing off all leading |
|
// space characters including padding. |
|
func (t *Segment) TrimLeftSpace(buffer []byte) Segment { |
|
v := buffer[t.Start:t.Stop] |
|
l := util.TrimLeftSpaceLength(v) |
|
return NewSegment(t.Start+l, t.Stop) |
|
} |
|
|
|
// TrimLeftSpaceWidth returns a new segment by slicing off leading space |
|
// characters until the given width. |
|
func (t *Segment) TrimLeftSpaceWidth(width int, buffer []byte) Segment { |
|
padding := t.Padding |
|
for ; width > 0; width-- { |
|
if padding == 0 { |
|
break |
|
} |
|
padding-- |
|
} |
|
if width == 0 { |
|
return NewSegmentPadding(t.Start, t.Stop, padding) |
|
} |
|
text := buffer[t.Start:t.Stop] |
|
start := t.Start |
|
for _, c := range text { |
|
if start >= t.Stop-1 || width <= 0 { |
|
break |
|
} |
|
if c == ' ' { |
|
width-- |
|
} else if c == '\t' { |
|
width -= 4 |
|
} else { |
|
break |
|
} |
|
start++ |
|
} |
|
if width < 0 { |
|
padding = width * -1 |
|
} |
|
return NewSegmentPadding(start, t.Stop, padding) |
|
} |
|
|
|
// WithStart returns a new Segment with same value except Start. |
|
func (t *Segment) WithStart(v int) Segment { |
|
return NewSegmentPadding(v, t.Stop, t.Padding) |
|
} |
|
|
|
// WithStop returns a new Segment with same value except Stop. |
|
func (t *Segment) WithStop(v int) Segment { |
|
return NewSegmentPadding(t.Start, v, t.Padding) |
|
} |
|
|
|
// ConcatPadding concats the padding to the given slice. |
|
func (t *Segment) ConcatPadding(v []byte) []byte { |
|
if t.Padding > 0 { |
|
return append(v, bytes.Repeat(space, t.Padding)...) |
|
} |
|
return v |
|
} |
|
|
|
// Segments is a collection of the Segment. |
|
type Segments struct { |
|
values []Segment |
|
} |
|
|
|
// NewSegments return a new Segments. |
|
func NewSegments() *Segments { |
|
return &Segments{ |
|
values: nil, |
|
} |
|
} |
|
|
|
// Append appends the given segment after the tail of the collection. |
|
func (s *Segments) Append(t Segment) { |
|
if s.values == nil { |
|
s.values = make([]Segment, 0, 20) |
|
} |
|
s.values = append(s.values, t) |
|
} |
|
|
|
// AppendAll appends all elements of given segments after the tail of the collection. |
|
func (s *Segments) AppendAll(t []Segment) { |
|
if s.values == nil { |
|
s.values = make([]Segment, 0, 20) |
|
} |
|
s.values = append(s.values, t...) |
|
} |
|
|
|
// Len returns the length of the collection. |
|
func (s *Segments) Len() int { |
|
if s.values == nil { |
|
return 0 |
|
} |
|
return len(s.values) |
|
} |
|
|
|
// At returns a segment at the given index. |
|
func (s *Segments) At(i int) Segment { |
|
return s.values[i] |
|
} |
|
|
|
// Set sets the given Segment. |
|
func (s *Segments) Set(i int, v Segment) { |
|
s.values[i] = v |
|
} |
|
|
|
// SetSliced replace the collection with a subsliced value. |
|
func (s *Segments) SetSliced(lo, hi int) { |
|
s.values = s.values[lo:hi] |
|
} |
|
|
|
// Sliced returns a subslice of the collection. |
|
func (s *Segments) Sliced(lo, hi int) []Segment { |
|
return s.values[lo:hi] |
|
} |
|
|
|
// Clear delete all element of the collection. |
|
func (s *Segments) Clear() { |
|
s.values = nil |
|
} |
|
|
|
// Unshift insert the given Segment to head of the collection. |
|
func (s *Segments) Unshift(v Segment) { |
|
s.values = append(s.values[0:1], s.values[0:]...) |
|
s.values[0] = v |
|
}
|
|
|