mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2025-04-08 02:01:50 +00:00
Fix ANSI processor to handle multi-line regions
This commit is contained in:
parent
f71ea5f3ea
commit
5e3cb3a4ea
10
src/ansi.go
10
src/ansi.go
@ -36,11 +36,13 @@ func init() {
|
|||||||
ansiRegex = regexp.MustCompile("\x1b\\[[0-9;]*[mK]")
|
ansiRegex = regexp.MustCompile("\x1b\\[[0-9;]*[mK]")
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractColor(str *string) (*string, []ansiOffset) {
|
func extractColor(str *string, state *ansiState) (*string, []ansiOffset, *ansiState) {
|
||||||
var offsets []ansiOffset
|
var offsets []ansiOffset
|
||||||
|
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
var state *ansiState
|
|
||||||
|
if state != nil {
|
||||||
|
offsets = append(offsets, ansiOffset{[2]int32{0, 0}, *state})
|
||||||
|
}
|
||||||
|
|
||||||
idx := 0
|
idx := 0
|
||||||
for _, offset := range ansiRegex.FindAllStringIndex(*str, -1) {
|
for _, offset := range ansiRegex.FindAllStringIndex(*str, -1) {
|
||||||
@ -76,7 +78,7 @@ func extractColor(str *string) (*string, []ansiOffset) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
outputStr := output.String()
|
outputStr := output.String()
|
||||||
return &outputStr, offsets
|
return &outputStr, offsets, state
|
||||||
}
|
}
|
||||||
|
|
||||||
func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
|
func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
|
||||||
|
@ -14,79 +14,89 @@ func TestExtractColor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
src := "hello world"
|
src := "hello world"
|
||||||
|
var state *ansiState
|
||||||
clean := "\x1b[0m"
|
clean := "\x1b[0m"
|
||||||
check := func(assertion func(ansiOffsets []ansiOffset)) {
|
check := func(assertion func(ansiOffsets []ansiOffset, state *ansiState)) {
|
||||||
output, ansiOffsets := extractColor(&src)
|
output, ansiOffsets, newState := extractColor(&src, state)
|
||||||
|
state = newState
|
||||||
if *output != "hello world" {
|
if *output != "hello world" {
|
||||||
t.Errorf("Invalid output: {}", output)
|
t.Errorf("Invalid output: {}", output)
|
||||||
}
|
}
|
||||||
fmt.Println(src, ansiOffsets, clean)
|
fmt.Println(src, ansiOffsets, clean)
|
||||||
assertion(ansiOffsets)
|
assertion(ansiOffsets, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
check(func(offsets []ansiOffset) {
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
if len(offsets) > 0 {
|
if len(offsets) > 0 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
state = nil
|
||||||
src = "\x1b[0mhello world"
|
src = "\x1b[0mhello world"
|
||||||
check(func(offsets []ansiOffset) {
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
if len(offsets) > 0 {
|
if len(offsets) > 0 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
state = nil
|
||||||
src = "\x1b[1mhello world"
|
src = "\x1b[1mhello world"
|
||||||
check(func(offsets []ansiOffset) {
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
if len(offsets) != 1 {
|
if len(offsets) != 1 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
assert(offsets[0], 0, 11, -1, -1, true)
|
assert(offsets[0], 0, 11, -1, -1, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
state = nil
|
||||||
src = "\x1b[1mhello \x1b[mworld"
|
src = "\x1b[1mhello \x1b[mworld"
|
||||||
check(func(offsets []ansiOffset) {
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
if len(offsets) != 1 {
|
if len(offsets) != 1 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
assert(offsets[0], 0, 6, -1, -1, true)
|
assert(offsets[0], 0, 6, -1, -1, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
state = nil
|
||||||
src = "\x1b[1mhello \x1b[Kworld"
|
src = "\x1b[1mhello \x1b[Kworld"
|
||||||
check(func(offsets []ansiOffset) {
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
if len(offsets) != 1 {
|
if len(offsets) != 1 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
assert(offsets[0], 0, 11, -1, -1, true)
|
assert(offsets[0], 0, 11, -1, -1, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
state = nil
|
||||||
src = "hello \x1b[34;45;1mworld"
|
src = "hello \x1b[34;45;1mworld"
|
||||||
check(func(offsets []ansiOffset) {
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
if len(offsets) != 1 {
|
if len(offsets) != 1 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
assert(offsets[0], 6, 11, 4, 5, true)
|
assert(offsets[0], 6, 11, 4, 5, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
state = nil
|
||||||
src = "hello \x1b[34;45;1mwor\x1b[34;45;1mld"
|
src = "hello \x1b[34;45;1mwor\x1b[34;45;1mld"
|
||||||
check(func(offsets []ansiOffset) {
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
if len(offsets) != 1 {
|
if len(offsets) != 1 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
assert(offsets[0], 6, 11, 4, 5, true)
|
assert(offsets[0], 6, 11, 4, 5, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
state = nil
|
||||||
src = "hello \x1b[34;45;1mwor\x1b[0mld"
|
src = "hello \x1b[34;45;1mwor\x1b[0mld"
|
||||||
check(func(offsets []ansiOffset) {
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
if len(offsets) != 1 {
|
if len(offsets) != 1 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
assert(offsets[0], 6, 9, 4, 5, true)
|
assert(offsets[0], 6, 9, 4, 5, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
state = nil
|
||||||
src = "hello \x1b[34;48;5;233;1mwo\x1b[38;5;161mr\x1b[0ml\x1b[38;5;161md"
|
src = "hello \x1b[34;48;5;233;1mwo\x1b[38;5;161mr\x1b[0ml\x1b[38;5;161md"
|
||||||
check(func(offsets []ansiOffset) {
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
if len(offsets) != 3 {
|
if len(offsets) != 3 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
@ -96,12 +106,47 @@ func TestExtractColor(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// {38,48};5;{38,48}
|
// {38,48};5;{38,48}
|
||||||
|
state = nil
|
||||||
src = "hello \x1b[38;5;38;48;5;48;1mwor\x1b[38;5;48;48;5;38ml\x1b[0md"
|
src = "hello \x1b[38;5;38;48;5;48;1mwor\x1b[38;5;48;48;5;38ml\x1b[0md"
|
||||||
check(func(offsets []ansiOffset) {
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
if len(offsets) != 2 {
|
if len(offsets) != 2 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
assert(offsets[0], 6, 9, 38, 48, true)
|
assert(offsets[0], 6, 9, 38, 48, true)
|
||||||
assert(offsets[1], 9, 10, 48, 38, true)
|
assert(offsets[1], 9, 10, 48, 38, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
src = "hello \x1b[32;1mworld"
|
||||||
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
|
if len(offsets) != 1 {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if state.fg != 2 || state.bg != -1 || !state.bold {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
assert(offsets[0], 6, 11, 2, -1, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
src = "hello world"
|
||||||
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
|
if len(offsets) != 1 {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if state.fg != 2 || state.bg != -1 || !state.bold {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
assert(offsets[0], 0, 11, 2, -1, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
src = "hello \x1b[0;38;5;200;48;5;100mworld"
|
||||||
|
check(func(offsets []ansiOffset, state *ansiState) {
|
||||||
|
if len(offsets) != 2 {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if state.fg != 200 || state.bg != 100 || state.bold {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
assert(offsets[0], 0, 6, 2, -1, true)
|
||||||
|
assert(offsets[1], 6, 11, 200, 100, false)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -69,14 +69,17 @@ func Run(opts *Options) {
|
|||||||
}
|
}
|
||||||
if opts.Ansi {
|
if opts.Ansi {
|
||||||
if opts.Theme != nil {
|
if opts.Theme != nil {
|
||||||
|
var state *ansiState
|
||||||
ansiProcessor = func(data *string) (*string, []ansiOffset) {
|
ansiProcessor = func(data *string) (*string, []ansiOffset) {
|
||||||
return extractColor(data)
|
trimmed, offsets, newState := extractColor(data, state)
|
||||||
|
state = newState
|
||||||
|
return trimmed, offsets
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// When color is disabled but ansi option is given,
|
// When color is disabled but ansi option is given,
|
||||||
// we simply strip out ANSI codes from the input
|
// we simply strip out ANSI codes from the input
|
||||||
ansiProcessor = func(data *string) (*string, []ansiOffset) {
|
ansiProcessor = func(data *string) (*string, []ansiOffset) {
|
||||||
trimmed, _ := extractColor(data)
|
trimmed, _, _ := extractColor(data, nil)
|
||||||
return trimmed, nil
|
return trimmed, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -378,6 +378,7 @@ func (t *Terminal) printHeader() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
max := C.MaxY()
|
max := C.MaxY()
|
||||||
|
var state *ansiState
|
||||||
for idx, lineStr := range t.header {
|
for idx, lineStr := range t.header {
|
||||||
if !t.reverse {
|
if !t.reverse {
|
||||||
idx = len(t.header) - idx - 1
|
idx = len(t.header) - idx - 1
|
||||||
@ -389,7 +390,8 @@ func (t *Terminal) printHeader() {
|
|||||||
if line >= max {
|
if line >= max {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
trimmed, colors := extractColor(&lineStr)
|
trimmed, colors, newState := extractColor(&lineStr, state)
|
||||||
|
state = newState
|
||||||
item := &Item{
|
item := &Item{
|
||||||
text: trimmed,
|
text: trimmed,
|
||||||
index: 0,
|
index: 0,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user