2019-03-13 04:14:30 -07:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
|
|
|
|
"golang.org/x/text/unicode/norm"
|
|
|
|
)
|
|
|
|
|
|
|
|
type engine struct {
|
|
|
|
settings *Settings
|
|
|
|
env environmentInfo
|
|
|
|
renderer *ColorWriter
|
|
|
|
activeBlock *Block
|
|
|
|
activeSegment *Segment
|
|
|
|
previousActiveSegment *Segment
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) getPowerlineColor(foreground bool) string {
|
|
|
|
if e.previousActiveSegment == nil {
|
2020-09-21 06:44:09 -07:00
|
|
|
return Transparent
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
if !foreground && e.activeSegment.Style != Powerline {
|
2020-09-21 06:44:09 -07:00
|
|
|
return Transparent
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
if foreground && e.previousActiveSegment.Style != Powerline {
|
2020-09-21 06:44:09 -07:00
|
|
|
return Transparent
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
return e.previousActiveSegment.Background
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) writePowerLineSeparator(background string, foreground string) {
|
|
|
|
if e.activeBlock.InvertPowerlineSeparatorColor {
|
|
|
|
e.renderer.write(foreground, background, e.activeBlock.PowerlineSeparator)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
e.renderer.write(background, foreground, e.activeBlock.PowerlineSeparator)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) endPowerline() {
|
|
|
|
if e.activeSegment != nil &&
|
|
|
|
e.activeSegment.Style != Powerline &&
|
|
|
|
e.previousActiveSegment != nil &&
|
|
|
|
e.previousActiveSegment.Style == Powerline {
|
|
|
|
e.writePowerLineSeparator(e.getPowerlineColor(false), e.previousActiveSegment.Background)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) renderPowerLineSegment(text string) {
|
|
|
|
e.writePowerLineSeparator(e.activeSegment.Background, e.getPowerlineColor(true))
|
|
|
|
e.renderText(text)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) renderPlainSegment(text string) {
|
|
|
|
e.renderText(text)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) renderDiamondSegment(text string) {
|
2020-09-21 06:44:09 -07:00
|
|
|
e.renderer.write(Transparent, e.activeSegment.Background, e.activeSegment.LeadingDiamond)
|
2019-03-13 04:14:30 -07:00
|
|
|
e.renderText(text)
|
2020-09-21 06:44:09 -07:00
|
|
|
e.renderer.write(Transparent, e.activeSegment.Background, e.activeSegment.TrailingDiamond)
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) getStringProperty(property Property, defaultValue string) string {
|
|
|
|
if value, ok := e.activeSegment.Properties[property]; ok {
|
|
|
|
return parseString(value, defaultValue)
|
|
|
|
}
|
|
|
|
return defaultValue
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) renderText(text string) {
|
|
|
|
prefix := e.getStringProperty(Prefix, " ")
|
|
|
|
postfix := e.getStringProperty(Postfix, " ")
|
|
|
|
e.renderer.write(e.activeSegment.Background, e.activeSegment.Foreground, fmt.Sprintf("%s%s%s", prefix, text, postfix))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) renderSegmentText(text string) {
|
|
|
|
switch e.activeSegment.Style {
|
|
|
|
case Plain:
|
|
|
|
e.renderPlainSegment(text)
|
|
|
|
case Diamond:
|
|
|
|
e.renderDiamondSegment(text)
|
|
|
|
default:
|
|
|
|
e.renderPowerLineSegment(text)
|
|
|
|
}
|
|
|
|
e.previousActiveSegment = e.activeSegment
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) renderBlockSegments(block *Block) string {
|
|
|
|
defer e.reset()
|
|
|
|
e.activeBlock = block
|
|
|
|
for _, segment := range block.Segments {
|
2020-09-17 07:51:29 -07:00
|
|
|
props, err := segment.mapSegmentWithWriter(e.env)
|
|
|
|
if err != nil || !segment.enabled() {
|
2019-03-13 04:14:30 -07:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
e.activeSegment = segment
|
|
|
|
e.endPowerline()
|
|
|
|
text := segment.string()
|
|
|
|
e.activeSegment.Background = props.background
|
|
|
|
e.activeSegment.Foreground = props.foreground
|
|
|
|
e.renderSegmentText(text)
|
|
|
|
}
|
|
|
|
if e.previousActiveSegment != nil && e.previousActiveSegment.Style == Powerline {
|
2020-09-21 06:44:09 -07:00
|
|
|
e.writePowerLineSeparator(Transparent, e.previousActiveSegment.Background)
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
return e.renderer.string()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) lenWithoutANSI(str string) int {
|
|
|
|
ansi := "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
|
|
|
|
re := regexp.MustCompile(ansi)
|
|
|
|
stripped := re.ReplaceAllString(str, "")
|
|
|
|
var i norm.Iter
|
|
|
|
i.InitString(norm.NFD, stripped)
|
|
|
|
var count int
|
|
|
|
for !i.Done() {
|
|
|
|
i.Next()
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
return count
|
|
|
|
}
|
|
|
|
|
2020-09-16 04:08:28 -07:00
|
|
|
func (e *engine) render() {
|
2019-03-13 04:14:30 -07:00
|
|
|
for _, block := range e.settings.Blocks {
|
|
|
|
// if line break, append a line break
|
|
|
|
if block.Type == LineBreak {
|
2020-09-16 04:08:28 -07:00
|
|
|
fmt.Printf("\x1b[%dC ", 1000)
|
2019-03-13 04:14:30 -07:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if block.LineOffset < 0 {
|
2020-09-16 04:08:28 -07:00
|
|
|
fmt.Printf("\x1b[%dF", -block.LineOffset)
|
2019-03-13 04:14:30 -07:00
|
|
|
} else if block.LineOffset > 0 {
|
2020-09-16 04:08:28 -07:00
|
|
|
fmt.Printf("\x1b[%dB", block.LineOffset)
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
switch block.Alignment {
|
|
|
|
case Right:
|
2020-09-16 04:08:28 -07:00
|
|
|
fmt.Printf("\x1b[%dC", 1000)
|
2019-03-13 04:14:30 -07:00
|
|
|
blockText := e.renderBlockSegments(block)
|
2020-09-16 04:08:28 -07:00
|
|
|
fmt.Printf("\x1b[%dD", e.lenWithoutANSI(blockText)+e.settings.RightSegmentOffset)
|
|
|
|
fmt.Print(blockText)
|
2019-03-13 04:14:30 -07:00
|
|
|
default:
|
2020-09-16 04:08:28 -07:00
|
|
|
fmt.Print(e.renderBlockSegments(block))
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if e.settings.EndSpaceEnabled {
|
2020-09-16 04:08:28 -07:00
|
|
|
fmt.Print(" ")
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) reset() {
|
|
|
|
e.renderer.reset()
|
|
|
|
e.previousActiveSegment = nil
|
|
|
|
e.activeBlock = nil
|
|
|
|
}
|