Skip to content

Commit

Permalink
Merge branch 'flip-horizontal' of github.com:tmck-code/pokesay into f…
Browse files Browse the repository at this point in the history
…lip-horizontal
  • Loading branch information
tmck-code committed Dec 26, 2024
2 parents 4aec8e9 + be483f9 commit 53296e2
Show file tree
Hide file tree
Showing 2 changed files with 224 additions and 82 deletions.
191 changes: 129 additions & 62 deletions src/pokesay/print.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,84 +233,151 @@ type ANSILineToken struct {
Text string
}

func TokeniseANSIString(line string) []ANSILineToken {
tokens := make([]ANSILineToken, 0)
var inAnsiCode bool

currentColour := ""
currentForeground := ""
currentBackground := ""
currentText := ""

for i, r := range line {
if r == '\x1b' {
if currentText != "" {
if currentColour == "\033[0m" || currentColour == "\033[49m" {
tokens = append(tokens, ANSILineToken{currentColour, currentText})
} else {
tokens = append(tokens, ANSILineToken{currentBackground + currentForeground, currentText})
// func TokeniseANSIString(line string) [][]ANSILineToken {
// var inAnsiCode bool
// lines := make([][]ANSILineToken, 0)

// currentColour := ""
// currentForeground := ""
// currentBackground := ""

// for i, line := range strings.Split(line, "\n") {
// currentText := ""
// tokens := make([]ANSILineToken, 0)

// for _, r := range line {
// if r == '\x1b' {
// if currentText != "" {
// if currentColour == "\033[0m" || currentColour == "\033[49m" {
// tokens = append(tokens, ANSILineToken{currentColour, currentText})
// } else {
// tokens = append(tokens, ANSILineToken{currentBackground + currentForeground, currentText})
// }
// currentColour = ""
// currentText = ""
// }
// if i < len(line)-1 && line[i+1] == '[' {
// inAnsiCode = true
// currentColour = "\x1b"
// }
// continue
// }
// if inAnsiCode {
// currentColour += string(r)
// if r == 'm' {
// inAnsiCode = false
// if strings.Contains(currentColour, "38;5;") {
// currentForeground = currentColour
// } else if strings.Contains(currentColour, "48;5;") {
// currentBackground = currentColour
// } else {
// currentForeground = currentColour
// currentBackground = ""
// }
// }
// } else {
// currentText += string(r)
// }
// }
// if len(currentText) > 0 {
// tokens = append(tokens, ANSILineToken{currentBackground + currentForeground, currentText})
// }
// tokens = append(tokens, ANSILineToken{"\033[0m", ""})
// lines = append(lines, tokens)
// }
// return lines
// }

func TokeniseANSIString(msg string) [][]ANSILineToken {
var isColour bool
var colour string
var fg string
var bg string

// var tokens []string
var tokens []ANSILineToken
// var lines [][]string
var lines [][]ANSILineToken
for _, line := range strings.Split(msg, "\n") {
var text string

for _, ch := range line {
if ch == '\033' {
if text != "" {
tokens = append(tokens, ANSILineToken{bg + fg, text})
colour = ""
text = ""
}
currentColour = ""
currentText = ""
}
if i < len(line)-1 && line[i+1] == '[' {
inAnsiCode = true
currentColour = "\x1b"
}
continue
}
if inAnsiCode {
currentColour += string(r)
if r == 'm' {
inAnsiCode = false
if strings.Contains(currentColour, "38;5;") {
currentForeground = currentColour
} else if strings.Contains(currentColour, "48;5;") {
currentBackground = currentColour
} else {
currentForeground = currentColour
currentBackground = ""
isColour = true
colour = string(ch)
} else if isColour {
colour += string(ch)
if ch == 'm' {
isColour = false
if strings.Contains(colour, "38;5;") {
fg = colour
} else if strings.Contains(colour, "48;5;") {
bg = colour
} else {
fg = colour
bg = ""
}
}
} else {
text += string(ch)
}
} else {
currentText += string(r)
}
if text != "" {
tokens = append(tokens, ANSILineToken{bg + fg, text})
}
if (colour != "") && len(tokens) > 0 {
tokens = append(tokens, ANSILineToken{"\033[0m", ""})
}
lines = append(lines, tokens)
tokens = nil
}
tokens = append(tokens, ANSILineToken{currentColour, currentText})
return tokens
return lines
}

func ReverseANSIString(line string) string {
tokens := TokeniseANSIString(line)
lines := TokeniseANSIString(line)
reversed := ""

for i := len(tokens) - 1; i >= 0; i-- {
if tokens[i].Colour != "\033[0m" && (i < len(tokens)-1 && tokens[i+1].Colour != "\033[0m") {
reversed += "\033[0m"
}
if tokens[i].Colour == "\033[49m" && (i < len(tokens)-1 && tokens[i+1].Colour != "\033[0m") {
reversed += "\033[49m"
maxWidth := 0
widths := make([]int, len(lines))
for idx, l := range strings.Split(line, "\n") {
ln := UnicodeStringLength(l)
if ln > maxWidth {
maxWidth = ln
}
reversed += tokens[i].Colour + ReverseUnicodeString(tokens[i].Text)
widths[idx] = ln
}

return reversed
}

func ReverseANSIStrings(lines []string) []string {
reversed := make([]string, len(lines))

maxWidth := 0
for _, line := range lines {
l := UnicodeStringLength(line)
if l > maxWidth {
maxWidth = l
// for i, line := range lines {
// reversed[i] = strings.Repeat(" ", maxWidth-UnicodeStringLength(line)) + ReverseANSIString(line)
// }

for idx, tokens := range lines {
needsReset := false
// ensure vertical alignment
reversed += strings.Repeat(" ", maxWidth-widths[idx])
for i := len(tokens) - 1; i >= 0; i-- {
if tokens[i].Colour != "" {
needsReset = true
}
reversed += tokens[i].Colour + ReverseUnicodeString(tokens[i].Text)
}
if needsReset {
reversed += "\033[0m"
}
if idx < len(lines)-1 {
reversed += "\n"
}
}

for i, line := range lines {
reversed[i] = strings.Repeat(" ", maxWidth-UnicodeStringLength(line)) + ReverseANSIString(line)
if line[len(line)-1] == '\n' {
reversed += "\n"
}

return reversed
}

Expand Down
115 changes: 95 additions & 20 deletions test/pokesay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,57 @@ func TestUnicodeStringLength(test *testing.T) {
func TestTokeniseANSIString(test *testing.T) {
line := "\x1b[38;5;129mAAA \x1b[48;5;160m XX \x1b[0m"

expected := []pokesay.ANSILineToken{
pokesay.ANSILineToken{Colour: "\x1b[38;5;129m", Text: "AAA "},
pokesay.ANSILineToken{Colour: "\x1b[48;5;160m\x1b[38;5;129m", Text: " XX "},
pokesay.ANSILineToken{Colour: "\x1b[0m", Text: ""},
expected := [][]pokesay.ANSILineToken{
{
pokesay.ANSILineToken{Colour: "\x1b[38;5;129m", Text: "AAA "},
pokesay.ANSILineToken{Colour: "\x1b[48;5;160m\x1b[38;5;129m", Text: " XX "},
pokesay.ANSILineToken{Colour: "\x1b[0m", Text: ""},
},
}
result := pokesay.TokeniseANSIString(line)
Assert(expected, result, test)
}

func TestTokeniseANSIStringLines(test *testing.T) {
msg := "\x1b[38;5;160m▄ \x1b[38;5;46m▄\n\x1b[38;5;190m▄"

expected := [][]pokesay.ANSILineToken{
{ // Line 1
pokesay.ANSILineToken{Colour: "\x1b[38;5;160m", Text: "▄ "},
pokesay.ANSILineToken{Colour: "\x1b[38;5;46m", Text: "▄"},
pokesay.ANSILineToken{Colour: "\x1b[0m", Text: ""},
},
{ // Line 2
pokesay.ANSILineToken{Colour: "\x1b[38;5;46m", Text: "▄ "},
pokesay.ANSILineToken{Colour: "\x1b[38;5;190m", Text: "▄"},
pokesay.ANSILineToken{Colour: "\x1b[0m", Text: ""},
},
}
result := pokesay.TokeniseANSIString(msg)
Assert(expected, result, test)

}

func TestTokeniseANSILineWithTrailingSpaces(test *testing.T) {
// The AAA has a purple fg
// The XX has a red bg
line := " \x1b[38;5;129mAAA \x1b[48;5;160m XY \x1b[0m "

// The AAA should still have a purple fg
// The XX should still have a red bg
expected := [][]pokesay.ANSILineToken{
{
pokesay.ANSILineToken{Colour: "", Text: " "},
pokesay.ANSILineToken{Colour: "\x1b[38;5;129m", Text: "AAA "},
pokesay.ANSILineToken{Colour: "\x1b[48;5;160m\x1b[38;5;129m", Text: " XY "},
pokesay.ANSILineToken{Colour: "\x1b[0m", Text: " "},
pokesay.ANSILineToken{Colour: "\x1b[0m", Text: ""},
},
}
result := pokesay.TokeniseANSIString(line)
// fmt.Printf("expected: %#v\n", expected)
// fmt.Printf("result: %#v\n", result)

Assert(expected, result, test)
}

Expand All @@ -136,16 +181,34 @@ func TestFlipHorizontalLine(test *testing.T) {

// The AAA should still have a purple fg
// The XX should still have a red bg
expected := "\x1b[0m\x1b[48;5;160m\x1b[38;5;129m YX \033[0m\x1b[38;5;129m AAA"
expected := "\x1b[0m\x1b[48;5;160m\x1b[38;5;129m YX \x1b[38;5;129m AAA\x1b[0m"
result := pokesay.ReverseANSIString(line)

Assert(expected, result, test)
}

func TestFlipHorizontalLineWithTrailingSpaces(test *testing.T) {
// The AAA has a purple fg
// The XX has a red bg
line := " \x1b[38;5;129mAAA \x1b[48;5;160m XY \x1b[0m "

// The AAA should still have a purple fg
// The XX should still have a red bg
expected := "\x1b[0m\x1b[0m \x1b[48;5;160m\x1b[38;5;129m YX \x1b[38;5;129m AAA \x1b[0m"
result := pokesay.ReverseANSIString(line)

fmt.Printf("expected: %#v\n", expected)
fmt.Printf("result: %#v\n", result)

Assert(expected, result, test)
}

func TestTokeniseANSIStringWithNoColour(test *testing.T) {
msg := " ▄▄ ▄▄"
expected := []pokesay.ANSILineToken{
pokesay.ANSILineToken{Colour: "", Text: " ▄▄ ▄▄"},
expected := [][]pokesay.ANSILineToken{
{
pokesay.ANSILineToken{Colour: "", Text: " ▄▄ ▄▄"},
},
}
result := pokesay.TokeniseANSIString(msg)
Assert(expected, result, test)
Expand All @@ -158,6 +221,13 @@ func TestReverseUnicodeString(test *testing.T) {
Assert(expected, result, test)
}

func TestReverseUnicodeStringWithTrailingSpaces(test *testing.T) {
msg := " ▄▄ ▄▄ "
expected := " ▄▄ ▄▄ "
result := pokesay.ReverseUnicodeString(msg)
Assert(expected, result, test)
}

func TestFlipHorizontalWithoutColour(test *testing.T) {
msg := []string{
" ▄▄ ▄▄",
Expand All @@ -184,13 +254,14 @@ func TestFlipHorizontalWithoutColour(test *testing.T) {
" ▀▄ ▄▄▄▀ ",
" ▀▄▀▀ ",
}
results := pokesay.ReverseANSIStrings(msg)
results := pokesay.ReverseANSIString(strings.Join(msg, "\n"))

splitResults := strings.Split(results, "\n")
for i := 0; i < len(expected); i++ {
Assert(expected[i], results[i], test)
Assert(expected[i], splitResults[i], test)
}

Assert(expected, results, test)
Assert(strings.Join(expected, "\n"), results, test)
}

func TestFlipHorizontalWithColourContinuation(test *testing.T) {
Expand All @@ -199,17 +270,20 @@ func TestFlipHorizontalWithColourContinuation(test *testing.T) {
"▄ \x1b[38;5;190m▄",
}

result := pokesay.ReverseANSIStrings(msg)
result := pokesay.ReverseANSIString(strings.Join(msg, "\n"))

expected := []string{
"\x1b[38;5;46m▄ \x1b[38;5;160m▄",
"\x1b[38;5;190m▄ \x1b[38;5;46m▄",
}
expected := strings.Join(
[]string{
"\x1b[0m\x1b[38;5;46m▄\x1b[38;5;160m ▄\x1b[0m",
"\x1b[0m\x1b[38;5;190m▄\x1b[38;5;46m ▄\x1b[0m",
},
"\n",
)

data := map[string]string{
"msg": strings.Join(msg, "\n"),
"expected": strings.Join(expected, "\n"),
"result": strings.Join(result, "\n"),
"expected": expected,
"result": result,
}

for msg, d := range data {
Expand All @@ -233,7 +307,7 @@ func TestFlipHorizontal(test *testing.T) {
" ▀▀\x1b[48;5;214m▄\x1b[49m▀\x1b[39m\x1b[39m",
}
fmt.Println("msg:", msg)
results := pokesay.ReverseANSIStrings(msg)
results := pokesay.ReverseANSIString(strings.Join(msg, "\n"))

expected := []string{
" \x1b[38;5;16m▄▄\x1b[0m \x1b[0m\x1b[48;5;16m\x1b[38;5;232m ▄\x1b[0m\x1b[38;5;16m▄\x1b[0m \x1b[0m ",
Expand All @@ -253,11 +327,12 @@ func TestFlipHorizontal(test *testing.T) {
for i, line := range expected {
fmt.Println("expected:", i, line)
}
for i, line := range results {
splitResults := strings.Split(results, "\n")
for i, line := range splitResults {
fmt.Println("results:", i, line)
}
for i := 0; i < len(expected); i++ {
Assert(expected[i], results[i], test)
Assert(expected[i], splitResults[i], test)
}
Assert(expected, results, test)
}

0 comments on commit 53296e2

Please sign in to comment.