From 70cf0e60087b4e04e581e8bb04f5933ea7bb9d32 Mon Sep 17 00:00:00 2001 From: bloeys Date: Sat, 16 Jul 2022 22:21:07 +0400 Subject: [PATCH] Implement NextAnsiCode --- ansi.go | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 47 ------------------ 2 files changed, 144 insertions(+), 47 deletions(-) create mode 100755 ansi.go diff --git a/ansi.go b/ansi.go new file mode 100755 index 0000000..1ea4ad1 --- /dev/null +++ b/ansi.go @@ -0,0 +1,144 @@ +package main + +import ( + "bytes" + "fmt" + + "github.com/bloeys/gglm/gglm" +) + +// https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences +// For Control Sequence Introducer (CSI) commands, `ESC[` is followed by: +// Zero or more "parameter bytes" in the range 0x30–0x3F. +// Zero or more "intermediate bytes" in the range 0x20–0x2F. +// One "final byte" in the range 0x40–0x7E. +const ( + AnsiCsiParamBytesStart = 0x30 + AnsiCsiParamBytesEnd = 0x3F + AnsiCsiIntermBytesStart = 0x20 + AnsiCsiIntermBytesEnd = 0x2F + AnsiCsiFinalBytesStart = 0x40 + AnsiCsiFinalBytesEnd = 0x7E +) + +var ( + AnsiEscBytes = []byte{'\\', 'x', '1', 'b', '['} + AnsiEscBytesLen = len(AnsiEscBytes) +) + +func NextAnsiCode(arr []byte) (index int, code []byte) { + + // https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences + // For Control Sequence Introducer (CSI) commands, `ESC[` is followed by: + // Zero or more "parameter bytes" in the range 0x30–0x3F. + // Zero or more "intermediate bytes" in the range 0x20–0x2F. + // One "final byte" in the range 0x40–0x7E. + + const paramBytesRegion = 0 + const intermBytesRegion = 1 + + startOffset := 0 + for startOffset < len(arr)-1 { + + ansiEscIndex := bytes.Index(arr[startOffset:], AnsiEscBytes) + if ansiEscIndex == -1 { + return -1, nil + } + ansiEscIndex += startOffset + startOffset = ansiEscIndex + AnsiEscBytesLen + + // Now that we have found an ESC[, to parse the sequence we expect bytes in a specific order + // and a specific range. That is, a valid "paramter bytes" character in the interm bytes region + // is considered an invalid char and will invalidate the sequence + finalByteIndex := -1 + region := paramBytesRegion + for i := ansiEscIndex + AnsiEscBytesLen; i < len(arr); i++ { + + b := arr[i] + + if region == paramBytesRegion { + + if b >= AnsiCsiParamBytesStart && b <= AnsiCsiParamBytesEnd { + continue + } + + if b >= AnsiCsiIntermBytesStart && b <= AnsiCsiIntermBytesEnd { + region = intermBytesRegion + continue + } + + if b >= AnsiCsiFinalBytesStart && b <= AnsiCsiFinalBytesEnd { + finalByteIndex = i + break + } + + break + + } else { + + if b >= AnsiCsiIntermBytesStart && b <= AnsiCsiIntermBytesEnd { + continue + } + + if b >= AnsiCsiFinalBytesStart && b <= AnsiCsiFinalBytesEnd { + finalByteIndex = i + break + } + + break + + } + } + + //If we fail to parse this sequence we continue to search ahead in the string + if finalByteIndex == -1 { + continue + } + + return ansiEscIndex, arr[ansiEscIndex : finalByteIndex+1] + } + + return -1, nil +} + +func fgColorFromAnsiCode(code int) gglm.Vec4 { + + switch code { + case Ansi_Fg_Black: + return gglm.Vec4{} + case Ansi_Fg_Red: + return gglm.Vec4{Data: [4]float32{0.5, 0, 0, 1}} + case Ansi_Fg_Green: + return gglm.Vec4{Data: [4]float32{0, 0.5, 0, 1}} + case Ansi_Fg_Yellow: + return gglm.Vec4{Data: [4]float32{0.5, 0.5, 0, 1}} + case Ansi_Fg_Blue: + return gglm.Vec4{Data: [4]float32{0, 0, 0.5, 1}} + case Ansi_Fg_Magenta: + return gglm.Vec4{Data: [4]float32{0.5, 0, 0.5, 1}} + case Ansi_Fg_Cyan: + return gglm.Vec4{Data: [4]float32{0, 0.66, 0.66, 1}} + case Ansi_Fg_White: + return gglm.Vec4{Data: [4]float32{0.8, 0.8, 0.8, 1}} + case Ansi_Fg_Gray: + return gglm.Vec4{Data: [4]float32{0.5, 0.5, 0.5, 1}} + + case Ansi_Fg_Bright_Red: + return gglm.Vec4{Data: [4]float32{1, 0, 0, 1}} + case Ansi_Fg_Bright_Green: + return gglm.Vec4{Data: [4]float32{0, 1, 0, 1}} + case Ansi_Fg_Bright_Yellow: + return gglm.Vec4{Data: [4]float32{1, 1, 0, 1}} + case Ansi_Fg_Bright_Blue: + return gglm.Vec4{Data: [4]float32{0, 0, 1, 1}} + case Ansi_Fg_Bright_Magenta: + return gglm.Vec4{Data: [4]float32{1, 0, 1, 1}} + case Ansi_Fg_Bright_Cyan: + return gglm.Vec4{Data: [4]float32{0, 1, 1, 1}} + case Ansi_Fg_Bright_White: + return gglm.Vec4{Data: [4]float32{1, 1, 1, 1}} + + } + + panic("Invalid ansi code: " + fmt.Sprint(code)) +} diff --git a/main.go b/main.go index b20de68..2b28eea 100755 --- a/main.go +++ b/main.go @@ -98,11 +98,6 @@ var ( func main() { - // x := `Hi \x1b[31Hello \x1b[31mthere` - // beforeArr, code, afterArr := nextAnsiCode([]rune(x)) - // fmt.Printf("x=%s; beforeArr=%s; code=%s; afterArr=%s\n", x, string(beforeArr), string(code), string(afterArr)) - // return - err := engine.Init() if err != nil { panic("Failed to init engine. Err: " + err.Error()) @@ -445,48 +440,6 @@ func (p *program) DrawTextAnsiCodes(text []rune, pos gglm.Vec3) gglm.Vec3 { return pos } -func fgColorFromAnsiCode(code int) gglm.Vec4 { - - switch code { - case Ansi_Fg_Black: - return gglm.Vec4{} - case Ansi_Fg_Red: - return gglm.Vec4{Data: [4]float32{0.5, 0, 0, 1}} - case Ansi_Fg_Green: - return gglm.Vec4{Data: [4]float32{0, 0.5, 0, 1}} - case Ansi_Fg_Yellow: - return gglm.Vec4{Data: [4]float32{0.5, 0.5, 0, 1}} - case Ansi_Fg_Blue: - return gglm.Vec4{Data: [4]float32{0, 0, 0.5, 1}} - case Ansi_Fg_Magenta: - return gglm.Vec4{Data: [4]float32{0.5, 0, 0.5, 1}} - case Ansi_Fg_Cyan: - return gglm.Vec4{Data: [4]float32{0, 0.66, 0.66, 1}} - case Ansi_Fg_White: - return gglm.Vec4{Data: [4]float32{0.8, 0.8, 0.8, 1}} - case Ansi_Fg_Gray: - return gglm.Vec4{Data: [4]float32{0.5, 0.5, 0.5, 1}} - - case Ansi_Fg_Bright_Red: - return gglm.Vec4{Data: [4]float32{1, 0, 0, 1}} - case Ansi_Fg_Bright_Green: - return gglm.Vec4{Data: [4]float32{0, 1, 0, 1}} - case Ansi_Fg_Bright_Yellow: - return gglm.Vec4{Data: [4]float32{1, 1, 0, 1}} - case Ansi_Fg_Bright_Blue: - return gglm.Vec4{Data: [4]float32{0, 0, 1, 1}} - case Ansi_Fg_Bright_Magenta: - return gglm.Vec4{Data: [4]float32{1, 0, 1, 1}} - case Ansi_Fg_Bright_Cyan: - return gglm.Vec4{Data: [4]float32{0, 1, 1, 1}} - case Ansi_Fg_Bright_White: - return gglm.Vec4{Data: [4]float32{1, 1, 1, 1}} - - } - - panic("Invalid ansi code: " + fmt.Sprint(code)) -} - func (p *program) SyntaxHighlightAndDraw(text []rune, pos gglm.Vec3) gglm.Vec3 { startIndex := 0