From bd3e65df4dc3f46119fe502e1ad502ef6b7873ca Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Jan 2025 20:53:47 +0900 Subject: [PATCH] Trim unsupported OSC sequences (#4169) Fix #4169 --- src/ansi.go | 26 +++++++++++++++++--------- src/ansi_test.go | 14 +++++++++++--- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/ansi.go b/src/ansi.go index bf00905e..37d9c767 100644 --- a/src/ansi.go +++ b/src/ansi.go @@ -98,11 +98,11 @@ func isPrint(c uint8) bool { return '\x20' <= c && c <= '\x7e' } -func matchOperatingSystemCommand(s string) int { +func matchOperatingSystemCommand(s string, start int) int { // `\x1b][0-9][;:][[:print:]]+(?:\x1b\\\\|\x07)` - // ^ match starting here + // ^ match starting here after the first printable character // - i := 5 // prefix matched in nextAnsiEscapeSequence() + i := start // prefix matched in nextAnsiEscapeSequence() for ; i < len(s) && isPrint(s[i]); i++ { } if i < len(s) { @@ -156,7 +156,7 @@ func isCtrlSeqStart(c uint8) bool { // nextAnsiEscapeSequence returns the ANSI escape sequence and is equivalent to // calling FindStringIndex() on the below regex (which was originally used): // -// "(?:\x1b[\\[()][0-9;:?]*[a-zA-Z@]|\x1b][0-9][;:][[:print:]]+(?:\x1b\\\\|\x07)|\x1b.|[\x0e\x0f]|.\x08)" +// "(?:\x1b[\\[()][0-9;:?]*[a-zA-Z@]|\x1b][0-9]+[;:][[:print:]]+(?:\x1b\\\\|\x07)|\x1b.|[\x0e\x0f]|.\x08)" func nextAnsiEscapeSequence(s string) (int, int) { // fast check for ANSI escape sequences i := 0 @@ -191,12 +191,20 @@ Loop: } } - // match: `\x1b][0-9][;:][[:print:]]+(?:\x1b\\\\|\x07)` - if i+5 < len(s) && s[i+1] == ']' && isNumeric(s[i+2]) && - (s[i+3] == ';' || s[i+3] == ':') && isPrint(s[i+4]) { + // match: `\x1b][0-9]+[;:][[:print:]]+(?:\x1b\\\\|\x07)` + if i+5 < len(s) && s[i+1] == ']' { + j := 2 + // \x1b][0-9]+[;:][[:print:]]+(?:\x1b\\\\|\x07) + // ------ + for ; i+j < len(s) && isNumeric(s[i+j]); j++ { + } - if j := matchOperatingSystemCommand(s[i:]); j != -1 { - return i, i + j + // \x1b][0-9]+[;:][[:print:]]+(?:\x1b\\\\|\x07) + // --------------- + if j > 2 && i+j+1 < len(s) && (s[i+j] == ';' || s[i+j] == ':') && isPrint(s[i+j+1]) { + if k := matchOperatingSystemCommand(s[i:], j+2); k != -1 { + return i, i + k + } } } diff --git a/src/ansi_test.go b/src/ansi_test.go index 05d8131e..e3431231 100644 --- a/src/ansi_test.go +++ b/src/ansi_test.go @@ -337,17 +337,25 @@ func TestExtractColor(t *testing.T) { }) state = nil + var color24 tui.Color = (1 << 24) + (180 << 16) + (190 << 8) + 254 src = "\x1b[1mhello \x1b[22;1;38:2:180:190:254mworld" check(func(offsets *[]ansiOffset, state *ansiState) { if len(*offsets) != 2 { t.Fail() } - var color tui.Color = (1 << 24) + (180 << 16) + (190 << 8) + 254 - if state.fg != color || state.attr != 1 { + if state.fg != color24 || state.attr != 1 { t.Fail() } assert((*offsets)[0], 0, 6, -1, -1, true) - assert((*offsets)[1], 6, 11, color, -1, true) + assert((*offsets)[1], 6, 11, color24, -1, true) + }) + + src = "\x1b]133;A\x1b\\hello \x1b]133;C\x1b\\world" + check(func(offsets *[]ansiOffset, state *ansiState) { + if len(*offsets) != 1 { + t.Fail() + } + assert((*offsets)[0], 0, 11, color24, -1, true) }) }