src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/ui/style_regions.go (about)

     1  package ui
     2  
     3  import (
     4  	"sort"
     5  
     6  	"src.elv.sh/pkg/diag"
     7  )
     8  
     9  // StylingRegion represents a region to apply styling.
    10  type StylingRegion struct {
    11  	diag.Ranging
    12  	Styling  Styling
    13  	Priority int
    14  }
    15  
    16  // StyleRegions applies styling to the specified regions in s.
    17  //
    18  // The regions are sorted by start position. If multiple Regions share the same
    19  // starting position, the one with the highest priority is kept; the other
    20  // regions are removed. If a Region starts before the end of the previous
    21  // Region, it is also removed.
    22  func StyleRegions(s string, regions []StylingRegion) Text {
    23  	regions = fixRegions(regions)
    24  
    25  	var text Text
    26  	lastTo := 0
    27  	for _, r := range regions {
    28  		if r.From > lastTo {
    29  			// Add text between regions or before the first region.
    30  			text = append(text, &Segment{Text: s[lastTo:r.From]})
    31  		}
    32  		text = append(text,
    33  			StyleSegment(&Segment{Text: s[r.From:r.To]}, r.Styling))
    34  		lastTo = r.To
    35  	}
    36  	if len(s) > lastTo {
    37  		// Add text after the last region.
    38  		text = append(text, &Segment{Text: s[lastTo:]})
    39  	}
    40  	return text
    41  }
    42  
    43  func fixRegions(regions []StylingRegion) []StylingRegion {
    44  	regions = append([]StylingRegion(nil), regions...)
    45  	// Sort regions by their start positions. Regions with the same start
    46  	// position are sorted by decreasing priority.
    47  	sort.Slice(regions, func(i, j int) bool {
    48  		a, b := regions[i], regions[j]
    49  		return a.From < b.From || (a.From == b.From && a.Priority > b.Priority)
    50  	})
    51  	// Remove overlapping regions, preferring the ones that appear earlier.
    52  	var newRegions []StylingRegion
    53  	lastTo := 0
    54  	for _, r := range regions {
    55  		if r.From < lastTo {
    56  			// Overlaps with the last one
    57  			continue
    58  		}
    59  		newRegions = append(newRegions, r)
    60  		lastTo = r.To
    61  	}
    62  	return newRegions
    63  }