list.go
func (l *List) Display(where Location) {
	if len(l.content) == 0 || l.visibility == VisibilityHide {
		return
	}

	c := l.content

	// XXX this isn't 100% correct since it doesn't handle wrapping lines.
	switch where {
	case Current:
	case Top:
		if len(c) > l.trueH {
			l.at = 0
			l.paging = true
		}
	case Bottom:
		l.at = len(c) - l.trueH
		if l.at < 0 {
			l.at = 0
		}
		l.paging = false

	case Up:
		l.at = l.at - l.trueH + 1
		if l.at < 0 {
			l.at = 0
		}
		l.paging = true

	case Down:
		y := l.at + l.trueH - 1
		if y+l.trueH > len(c) {
			l.Display(Bottom)
			return
		}
		l.at = y
		l.paging = true

	default:
		return
	}

	c = c[l.at : l.at+l.trueH]

	// create a buffer with all lines neatly clipped
	buffer := make([][]rune, 0, l.trueH*2)
	for _, s := range c {
		printWidth := 0
		start := 0
		var lastColor, leftover string
		var cc string // color continuation on next line
		for i := 0; i < len(s); {
			r, width := utf8.DecodeRuneInString(s[i:])
			if r == '\x1b' {
				_, skip, err := DecodeColor(s[i:])
				if err == nil {
					lastColor = s[i : i+skip]
					i += skip
					leftover = s[start:i]
					continue
				}
			}
			i += width
			printWidth++
			if printWidth > l.trueW-1 {
				// clip, so reset start and printWidth
				buffer = append(buffer,
					[]rune(lastColor+s[start:i]))
				start = i
				printWidth = 0
				cc = lastColor
				if start == len(s) {
					// we ended exactly with a color on
					// term boundary, clear out leftover
					// that was set in lastColor check
					leftover = ""
					break
				}
				continue
			} else if i < len(s) {
				// we do this unecessary song and dance to only
				// assign leftover once
				continue
			}
			leftover = s[start:i]
			break // will always break but do it anyway for clarity
		}
		if leftover != "" {
			// done clipping, next line
			filler := strings.Repeat(" ", l.trueW-printWidth)
			buffer = append(buffer, []rune(cc+leftover+filler))
		}
	}

	// now clip buffer to widget l.trueH; we only want to show bottom
	// l.trueH lines
	if len(buffer) > l.trueH {
		buffer = buffer[len(buffer)-l.trueH:]
	}
	x := 0
	for i, v := range buffer {
		l.w.printf(x, l.trueY+i, l.attr, "%v", string(v))
	}
}
func (l *List) Display(where Location) {
	if len(l.content) == 0 || l.visibility == VisibilityHide {
		return
	}

	c := l.content

	// XXX this isn't 100% correct since it doesn't handle wrapping lines.
	switch where {
	case Current:
	case Top:
		if len(c) > l.trueH {
			l.at = 0
			l.paging = true
		}
	case Bottom:
		l.at = len(c) - l.trueH
		if l.at < 0 {
			l.at = 0
		}
		l.paging = false

	case Up:
		l.at = l.at - l.trueH + 1
		if l.at < 0 {
			l.at = 0
		}
		l.paging = true

	case Down:
		y := l.at + l.trueH - 1
		if y+l.trueH > len(c) {
			l.Display(Bottom)
			return
		}
		l.at = y
		l.paging = true

	default:
		return
	}

	c = c[l.at : l.at+l.trueH]

	// create a buffer with all lines neatly clipped
	buffer := make([][]rune, 0, l.trueH*2)
	for _, s := range c {
		printWidth := 0
		start := 0
		var lastColor, leftover string
		var cc string // color continuation on next line
		for i := 0; i < len(s); {
			r, width := utf8.DecodeRuneInString(s[i:])
			if r == '\x1b' {
				_, skip, err := DecodeColor(s[i:])
				if err == nil {
					lastColor = s[i : i+skip]
					i += skip
					leftover = s[start:i]
					continue
				}
			}
			i += width
			printWidth++
			if printWidth > l.trueW-1 {
				// clip, so reset start and printWidth
				buffer = append(buffer,
					[]rune(lastColor+s[start:i]))
				start = i
				printWidth = 0
				cc = lastColor
				if start == len(s) {
					// we ended exactly with a color on
					// term boundary, clear out leftover
					// that was set in lastColor check
					leftover = ""
					break
				}
				continue
			} else if i < len(s) {
				// we do this unecessary song and dance to only
				// assign leftover once
				continue
			}
			leftover = s[start:i]
			break // will always break but do it anyway for clarity
		}
		if leftover != "" {
			// done clipping, next line
			filler := strings.Repeat(" ", l.trueW-printWidth)
			buffer = append(buffer, []rune(cc+leftover+filler))
		}
	}

	// now clip buffer to widget l.trueH; we only want to show bottom
	// l.trueH lines
	if len(buffer) > l.trueH {
		buffer = buffer[len(buffer)-l.trueH:]
	}
	x := 0
	for i, v := range buffer {
		l.w.printf(x, l.trueY+i, l.attr, "%v", string(v))
	}
}
// Display renders the widget.  This is called from queue context so be careful
// to not use blocking calls.