| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- package codec
- import (
- "fmt"
- )
- // EncodedSegment represents a portion of text that is encoded in some way.
- type EncodedSegment struct {
- // predecessors are all of the segments from the previous decoding pass
- predecessors []*EncodedSegment
- // original start/end indices before decoding
- original startEnd
- // encoded start/end indices relative to the previous decoding pass.
- // If it's a top level segment, original and encoded will be the
- // same.
- encoded startEnd
- // decoded start/end indices in this pass after decoding
- decoded startEnd
- // decodedValue contains the decoded string for this segment
- decodedValue string
- // encodings is the encodings that make up this segment. encodingKind
- // can be or'd together to hold multiple encodings
- encodings encodingKind
- // depth is how many decoding passes it took to decode this segment
- depth int
- }
- // Tags returns additional meta data tags related to the types of segments
- func Tags(segments []*EncodedSegment) []string {
- // Return an empty list if we don't have any segments
- if len(segments) == 0 {
- return []string{}
- }
- // Since decoding is done in passes, the depth of all the segments
- // should be the same
- depth := segments[0].depth
- // Collect the encodings from the segments
- encodings := segments[0].encodings
- for i := 1; i < len(segments); i++ {
- encodings |= segments[i].encodings
- }
- kinds := encodings.kinds()
- tags := make([]string, len(kinds)+1)
- tags[len(tags)-1] = fmt.Sprintf("decode-depth:%d", depth)
- for i, kind := range kinds {
- tags[i] = fmt.Sprintf("decoded:%s", kind)
- }
- return tags
- }
- // CurrentLine returns from the start of the line containing the segments
- // to the end of the line where the segment ends.
- func CurrentLine(segments []*EncodedSegment, currentRaw string) string {
- // Return the whole thing if no segments are provided
- if len(segments) == 0 {
- return currentRaw
- }
- start := 0
- end := len(currentRaw)
- // Merge the ranges together into a single decoded value
- decoded := segments[0].decoded
- for i := 1; i < len(segments); i++ {
- decoded = decoded.merge(segments[i].decoded)
- }
- // Find the start of the range
- for i := decoded.start; i > -1; i-- {
- c := currentRaw[i]
- if c == '\n' {
- start = i
- break
- }
- }
- // Find the end of the range
- for i := decoded.end; i < end; i++ {
- c := currentRaw[i]
- if c == '\n' {
- end = i
- break
- }
- }
- return currentRaw[start:end]
- }
- // AdjustMatchIndex maps a match index from the current decode pass back to
- // its location in the original text
- func AdjustMatchIndex(segments []*EncodedSegment, matchIndex []int) []int {
- // Don't adjust if we're not provided any segments
- if len(segments) == 0 {
- return matchIndex
- }
- // Map the match to the location in the original text
- match := startEnd{matchIndex[0], matchIndex[1]}
- // Map the match to its orignal location
- adjusted := toOriginal(segments, match)
- // Return the adjusted match index
- return []int{
- adjusted.start,
- adjusted.end,
- }
- }
- // SegmentsWithDecodedOverlap the segments where the start and end overlap its
- // decoded range
- func SegmentsWithDecodedOverlap(segments []*EncodedSegment, start, end int) []*EncodedSegment {
- se := startEnd{start, end}
- overlaps := []*EncodedSegment{}
- for _, segment := range segments {
- if segment.decoded.overlaps(se) {
- overlaps = append(overlaps, segment)
- }
- }
- return overlaps
- }
- // toOriginal maps a start/end to its start/end in the original text
- // the provided start/end should be relative to the segment's decoded value
- func toOriginal(predecessors []*EncodedSegment, decoded startEnd) startEnd {
- if len(predecessors) == 0 {
- return decoded
- }
- // Map the decoded value one level up where it was encoded
- encoded := startEnd{}
- for _, p := range predecessors {
- if !p.decoded.overlaps(decoded) {
- continue // Not in scope
- }
- // If fully contained, return the segments original start/end
- if p.decoded.contains(decoded) {
- return p.original
- }
- // Map the value to be relative to the predecessors's decoded values
- if encoded.end == 0 {
- encoded = p.encoded.add(p.decoded.overflow(decoded))
- } else {
- encoded = encoded.merge(p.encoded.add(p.decoded.overflow(decoded)))
- }
- }
- // Should only get here if the thing passed in wasn't in a decoded
- // value. This shouldn't be the case
- if encoded.end == 0 {
- return decoded
- }
- // Climb up another level
- // (NOTE: each segment references all the predecessors)
- return toOriginal(predecessors[0].predecessors, encoded)
- }
|