fzf/src/ansi.go

199 lines
4.2 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/tui"
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 tui.Color
bg tui.Color
attr tui.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() {
/*
References:
- https://github.com/gnachman/iTerm2
- http://ascii-table.com/ansi-escape-sequences.php
- http://ascii-table.com/ansi-escape-sequences-vt-100.php
- http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html
*/
// The following regular expression will include not all but most of the
// frequently used ANSI sequences
ansiRegex = regexp.MustCompile("\x1b[\\[()][0-9;]*[a-zA-Z@]|\x1b.|[\x0e\x0f]|.\x08")
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
}
if ansiCode[0] != '\x1b' || 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:
2016-11-23 15:13:10 +00:00
state.attr = state.attr | tui.Bold
case 2:
2016-11-23 15:13:10 +00:00
state.attr = state.attr | tui.Dim
case 3:
state.attr = state.attr | tui.Italic
case 4:
2016-11-23 15:13:10 +00:00
state.attr = state.attr | tui.Underline
case 5:
2016-11-23 15:13:10 +00:00
state.attr = state.attr | tui.Blink
case 7:
2016-11-23 15:13:10 +00:00
state.attr = state.attr | tui.Reverse
2015-03-18 16:59:14 +00:00
case 0:
init()
default:
if num >= 30 && num <= 37 {
state.fg = tui.Color(num - 30)
2015-03-18 16:59:14 +00:00
} else if num >= 40 && num <= 47 {
state.bg = tui.Color(num - 40)
} else if num >= 90 && num <= 97 {
state.fg = tui.Color(num - 90 + 8)
} else if num >= 100 && num <= 107 {
state.bg = tui.Color(num - 100 + 8)
2015-03-18 16:59:14 +00:00
}
}
case 1:
switch num {
case 2:
state256 = 10 // MAGIC
2015-03-18 16:59:14 +00:00
case 5:
state256++
default:
state256 = 0
}
case 2:
*ptr = tui.Color(num)
2015-03-18 16:59:14 +00:00
state256 = 0
case 10:
*ptr = tui.Color(1<<24) | tui.Color(num<<16)
state256++
case 11:
*ptr = *ptr | tui.Color(num<<8)
state256++
case 12:
*ptr = *ptr | tui.Color(num)
state256 = 0
2015-03-18 16:59:14 +00:00
}
}
}
if state256 > 0 {
*ptr = -1
}
2015-03-18 16:59:14 +00:00
return state
}