fzf/src/ansi.go

174 lines
3.3 KiB
Go
Raw Normal View History

2015-03-18 16:59:14 +00:00
package fzf
import (
"bytes"
"regexp"
"strconv"
"strings"
"unicode/utf8"
"github.com/junegunn/fzf/src/curses"
2015-03-18 16:59:14 +00:00
)
2015-03-22 07:05:54 +00:00
type ansiOffset struct {
2015-03-18 16:59:14 +00:00
offset [2]int32
color ansiState
}
type ansiState struct {
fg int
bg int
attr curses.Attr
2015-03-18 16:59:14 +00:00
}
func (s *ansiState) colored() bool {
return s.fg != -1 || s.bg != -1 || s.attr > 0
2015-03-18 16:59:14 +00:00
}
func (s *ansiState) equals(t *ansiState) bool {
if t == nil {
return !s.colored()
}
return s.fg == t.fg && s.bg == t.bg && s.attr == t.attr
2015-03-18 16:59:14 +00:00
}
var ansiRegex *regexp.Regexp
func init() {
2016-09-25 10:03:08 +00:00
ansiRegex = regexp.MustCompile("\x1b.[0-9;]*.")
2015-03-18 16:59:14 +00:00
}
func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, *[]ansiOffset, *ansiState) {
2015-03-22 07:05:54 +00:00
var offsets []ansiOffset
2015-03-18 16:59:14 +00:00
var output bytes.Buffer
if state != nil {
offsets = append(offsets, ansiOffset{[2]int32{0, 0}, *state})
}
2015-03-18 16:59:14 +00:00
idx := 0
for _, offset := range ansiRegex.FindAllStringIndex(str, -1) {
prev := str[idx:offset[0]]
output.WriteString(prev)
if proc != nil && !proc(prev, state) {
return "", nil, nil
}
newState := interpretCode(str[offset[0]:offset[1]], state)
2015-03-18 16:59:14 +00:00
if !newState.equals(state) {
if state != nil {
// Update last offset
2015-05-21 17:20:10 +00:00
(&offsets[len(offsets)-1]).offset[1] = int32(utf8.RuneCount(output.Bytes()))
2015-03-18 16:59:14 +00:00
}
if newState.colored() {
// Append new offset
state = newState
newLen := int32(utf8.RuneCount(output.Bytes()))
2015-03-22 07:05:54 +00:00
offsets = append(offsets, ansiOffset{[2]int32{newLen, newLen}, *state})
2015-03-18 16:59:14 +00:00
} else {
// Discard state
state = nil
}
}
idx = offset[1]
}
rest := str[idx:]
2015-03-18 16:59:14 +00:00
if len(rest) > 0 {
output.WriteString(rest)
if state != nil {
// Update last offset
(&offsets[len(offsets)-1]).offset[1] = int32(utf8.RuneCount(output.Bytes()))
2015-03-18 16:59:14 +00:00
}
}
if proc != nil {
proc(rest, state)
}
if len(offsets) == 0 {
return output.String(), nil, state
}
return output.String(), &offsets, state
2015-03-18 16:59:14 +00:00
}
func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
// State
var state *ansiState
if prevState == nil {
state = &ansiState{-1, -1, 0}
2015-03-18 16:59:14 +00:00
} else {
state = &ansiState{prevState.fg, prevState.bg, prevState.attr}
2015-03-18 16:59:14 +00:00
}
2016-09-25 10:03:08 +00:00
if ansiCode[1] != '[' || ansiCode[len(ansiCode)-1] != 'm' {
2015-03-27 03:35:06 +00:00
return state
}
2015-03-18 16:59:14 +00:00
ptr := &state.fg
state256 := 0
init := func() {
state.fg = -1
state.bg = -1
state.attr = 0
2015-03-18 16:59:14 +00:00
state256 = 0
}
ansiCode = ansiCode[2 : len(ansiCode)-1]
if len(ansiCode) == 0 {
init()
}
2015-03-18 16:59:14 +00:00
for _, code := range strings.Split(ansiCode, ";") {
if num, err := strconv.Atoi(code); err == nil {
switch state256 {
case 0:
switch num {
case 38:
ptr = &state.fg
state256++
case 48:
ptr = &state.bg
state256++
case 39:
state.fg = -1
case 49:
state.bg = -1
case 1:
state.attr = curses.Bold
case 2:
state.attr = curses.Dim
case 4:
state.attr = curses.Underline
case 5:
state.attr = curses.Blink
case 7:
state.attr = curses.Reverse
2015-03-18 16:59:14 +00:00
case 0:
init()
default:
if num >= 30 && num <= 37 {
state.fg = num - 30
} else if num >= 40 && num <= 47 {
state.bg = num - 40
} else if num >= 90 && num <= 97 {
state.fg = num - 90 + 8
} else if num >= 100 && num <= 107 {
state.bg = num - 100 + 8
2015-03-18 16:59:14 +00:00
}
}
case 1:
switch num {
case 5:
state256++
default:
state256 = 0
}
case 2:
*ptr = num
state256 = 0
}
}
}
return state
}