diff --git a/build.gradle.kts b/build.gradle.kts index c6142b29c5e56e53b7a1115b4cea5e93b4e53534..5e37aff4dbe9ceeb7316bc0a898d5f56dffa6530 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,4 +15,4 @@ plugins { } group = "de.justjanne.libquassel" -version = "0.9.2" +version = "0.10.0" diff --git a/libquassel-irc/build.gradle.kts b/libquassel-irc/build.gradle.kts new file mode 100644 index 0000000000000000000000000000000000000000..a268c30643e075880a270256f1ed2f8e8b9b93e0 --- /dev/null +++ b/libquassel-irc/build.gradle.kts @@ -0,0 +1,13 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +plugins { + id("justjanne.kotlin") + id("justjanne.publication") +} diff --git a/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/IrcFormat.kt b/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/IrcFormat.kt new file mode 100644 index 0000000000000000000000000000000000000000..d0ab78c8141a417786b090e3d41c084d7689c81a --- /dev/null +++ b/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/IrcFormat.kt @@ -0,0 +1,67 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.irc + +import de.justjanne.libquassel.irc.extensions.joinString + +object IrcFormat { + data class Span( + val content: String, + val style: Style = Style() + ) { + override fun toString(): String = joinString(", ", "Info(", ")") { + append(content) + if (style != Style()) { + append("style=$style") + } + } + } + + data class Style( + val flags: Set<Flag> = emptySet(), + val foreground: Color? = null, + val background: Color? = null, + ) { + fun flipFlag(flag: Flag) = copy( + flags = if (flags.contains(flag)) flags - flag else flags + flag + ) + + override fun toString(): String = joinString(", ", "Info(", ")") { + if (flags.isNotEmpty()) { + append("flags=$flags") + } + if (foreground != null) { + append("foreground=$foreground") + } + if (background != null) { + append("background=$background") + } + } + } + + sealed class Color { + data class Mirc(val index: Int) : Color() { + override fun toString(): String = "Mirc($index)" + } + + data class Hex(val color: Int) : Color() { + override fun toString(): String = "Hex(#${color.toString(16)})" + } + } + + enum class Flag { + BOLD, + ITALIC, + UNDERLINE, + STRIKETHROUGH, + MONOSPACE, + INVERSE + } +} diff --git a/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/IrcFormatDeserializer.kt b/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/IrcFormatDeserializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..8350bbc9e4c9ef8ede5f4fc06bb3224f3b7f2d42 --- /dev/null +++ b/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/IrcFormatDeserializer.kt @@ -0,0 +1,149 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.irc + +import de.justjanne.libquassel.irc.extensions.collapse +import kotlin.math.min + +/** + * A helper class to turn mIRC formatted Strings into Android’s SpannableStrings with the same + * color and format codes + */ +object IrcFormatDeserializer { + + fun parse(content: String) = sequence { + var i = 0 + var lastProcessed = 0 + var current = IrcFormat.Style() + + suspend fun SequenceScope<IrcFormat.Span>.emit() { + if (lastProcessed != i) { + yield(IrcFormat.Span(content.substring(lastProcessed, i), current)) + lastProcessed = i + } + } + + suspend fun SequenceScope<IrcFormat.Span>.processFlag(flag: IrcFormat.Flag) { + emit() + current = current.flipFlag(flag) + lastProcessed = ++i + } + + suspend fun SequenceScope<IrcFormat.Span>.processColor( + length: Int, + radix: Int = 10, + range: IntRange? = null, + matcher: (Char) -> Boolean + ): Pair<Int, Int?>? { + emit() + + // Skip Color Code + lastProcessed = ++i + + val foregroundData = content.substring(i, min(i + length, content.length)) + .takeWhile(matcher) + val foreground = foregroundData.toIntOrNull(radix) + ?.takeIf { range == null || it in range } + ?: return null + + // Skip foreground + i += foregroundData.length + + val backgroundData = + if (i < content.length && content[i] == ',') + content.substring(i + 1, min(i + length + 1, content.length)) + .takeWhile(matcher) + else null + val background = backgroundData + ?.toIntOrNull(radix) + ?.takeIf { range == null || it in range } + + if (background != null) { + // Skip background and separator + i += backgroundData.length + 1 + } + + lastProcessed = i + + return Pair(foreground, background) + } + + while (i < content.length) { + when (content[i]) { + CODE_BOLD -> processFlag(IrcFormat.Flag.BOLD) + CODE_ITALIC -> processFlag(IrcFormat.Flag.ITALIC) + CODE_UNDERLINE -> processFlag(IrcFormat.Flag.UNDERLINE) + CODE_STRIKETHROUGH -> processFlag(IrcFormat.Flag.STRIKETHROUGH) + CODE_MONOSPACE -> processFlag(IrcFormat.Flag.MONOSPACE) + CODE_SWAP, CODE_SWAP_KVIRC -> processFlag(IrcFormat.Flag.INVERSE) + CODE_COLOR -> { + val color = processColor(length = 2, range = 0..99) { + it in '0'..'9' + } + + current = if (color == null) { + current.copy(foreground = null, background = null) + } else { + val (foreground, background) = color + current.copy( + foreground = foreground.takeUnless { it == 99 }?.let { IrcFormat.Color.Mirc(it) }, + background = if (background == null) current.background + else background.takeUnless { it == 99 }?.let { IrcFormat.Color.Mirc(it) } + ) + } + } + CODE_HEXCOLOR -> { + val color = processColor(length = 6, radix = 16) { + it in '0'..'9' || it in 'a'..'f' || it in 'A'..'F' + } + + current = if (color == null) { + current.copy(foreground = null, background = null) + } else { + val (foreground, background) = color + current.copy( + foreground = IrcFormat.Color.Hex(foreground), + background = background?.let { + IrcFormat.Color.Hex(it) + } ?: current.background + ) + } + } + CODE_RESET -> { + emit() + current = IrcFormat.Style() + lastProcessed = ++i + } + else -> { + // Regular Character + i++ + } + } + } + + if (lastProcessed != content.length) { + yield(IrcFormat.Span(content.substring(lastProcessed), current)) + } + }.collapse { prev, current -> + if (prev.style == current.style) prev.copy(content = prev.content + current.content) + else null + } + + private const val CODE_BOLD = 0x02.toChar() + private const val CODE_COLOR = 0x03.toChar() + private const val CODE_HEXCOLOR = 0x04.toChar() + private const val CODE_ITALIC = 0x1D.toChar() + private const val CODE_UNDERLINE = 0x1F.toChar() + private const val CODE_STRIKETHROUGH = 0x1E.toChar() + private const val CODE_MONOSPACE = 0x11.toChar() + private const val CODE_SWAP_KVIRC = 0x12.toChar() + private const val CODE_SWAP = 0x16.toChar() + private const val CODE_RESET = 0x0F.toChar() +} diff --git a/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/backport/StringJoiner.kt b/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/backport/StringJoiner.kt new file mode 100644 index 0000000000000000000000000000000000000000..0a0d4eb123a5b7108bc73c17b20b962d312025bc --- /dev/null +++ b/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/backport/StringJoiner.kt @@ -0,0 +1,39 @@ +package de.justjanne.libquassel.irc.backport + +import java.io.Serializable + +internal class StringJoiner( + private val delimiter: String, + private val prefix: String = "", + private val suffix: String = "" +) : Serializable, Appendable { + private val builder = StringBuilder() + + override fun append(data: CharSequence?, start: Int, end: Int): StringJoiner = + this.apply { prepareBuilder().append(data, start, end) } + + override fun append(data: CharSequence?): StringJoiner = + this.apply { prepareBuilder().append(data) } + + override fun append(data: Char): StringJoiner = + this.apply { prepareBuilder().append(data) } + + private fun prepareBuilder(): StringBuilder = builder.apply { + append(if (isEmpty()) prefix else delimiter) + } + + override fun toString(): String = + if (builder.isEmpty()) { + prefix + suffix + } else { + val length = builder.length + builder.append(suffix) + val result = builder.toString() + builder.setLength(length) + result + } + + fun length(): Int = + if (builder.isEmpty()) prefix.length + suffix.length + else builder.length + suffix.length +} diff --git a/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/extensions/SequenceExtensions.kt b/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/extensions/SequenceExtensions.kt new file mode 100644 index 0000000000000000000000000000000000000000..c761234e5d6abd976df06aff90e8b5187cf10486 --- /dev/null +++ b/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/extensions/SequenceExtensions.kt @@ -0,0 +1,30 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.irc.extensions + +internal fun <T> Sequence<T>.collapse(callback: (T, T) -> T?) = sequence<T> { + var prev: T? = null + for (item in iterator()) { + if (prev != null) { + val collapsed = callback(prev, item) + if (collapsed == null) { + yield(prev) + prev = item + } else { + prev = collapsed + } + } else { + prev = item + } + } + if (prev != null) { + yield(prev) + } +} diff --git a/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/extensions/StringJoinerExtensions.kt b/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/extensions/StringJoinerExtensions.kt new file mode 100644 index 0000000000000000000000000000000000000000..315e7dd7c9b13c709cc74b7ecd5e082718e17cb2 --- /dev/null +++ b/libquassel-irc/src/main/kotlin/de/justjanne/libquassel/irc/extensions/StringJoinerExtensions.kt @@ -0,0 +1,12 @@ +package de.justjanne.libquassel.irc.extensions + +import de.justjanne.libquassel.irc.backport.StringJoiner + +internal inline fun joinString( + delimiter: String = "", + prefix: String = "", + suffix: String = "", + builderAction: StringJoiner.() -> Unit +): String { + return StringJoiner(delimiter, prefix, suffix).apply(builderAction).toString() +} diff --git a/libquassel-irc/src/test/kotlin/IrcFormatDeserializerTest.kt b/libquassel-irc/src/test/kotlin/IrcFormatDeserializerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..d5c4803a3a536c0820e2751e1c8b611f28261925 --- /dev/null +++ b/libquassel-irc/src/test/kotlin/IrcFormatDeserializerTest.kt @@ -0,0 +1,1591 @@ +package de.justjanne.libquassel.irc + +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class IrcFormatDeserializerTest { + @Test + fun testBroken() { + assertEquals( + emptyList(), + IrcFormatDeserializer.parse( + "\u000f" + ).toList() + ) + + assertEquals( + emptyList(), + IrcFormatDeserializer.parse( + "\u0003\u000f" + ).toList() + ) + assertEquals( + listOf( + IrcFormat.Span( + "[" + ), + IrcFormat.Span( + "hdf-us", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.ITALIC), + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + "] [" + ), + IrcFormat.Span( + "nd", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(7) + ) + ), + IrcFormat.Span( + "] blah blah blah" + ), + ), + IrcFormatDeserializer.parse( + "[\u001d\u000304hdf-us\u0003\u000f] [\u000307nd\u0003] blah blah blah" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "New Break set to: ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(2) + ) + ), + IrcFormat.Span( + "Target: ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span("388 "), + IrcFormat.Span( + "| ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(2) + ) + ), + IrcFormat.Span( + "Type: ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span("GS | "), + IrcFormat.Span( + "Break: ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span("58,000 "), + IrcFormat.Span( + "| ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(2) + ) + ), + IrcFormat.Span( + "120%: ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span("48,000 | "), + IrcFormat.Span( + "135%: ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span("43,000 "), + IrcFormat.Span( + "| ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(2) + ) + ), + IrcFormat.Span( + "145%: ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span("40,000 "), + IrcFormat.Span( + "| ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(2) + ) + ), + IrcFormat.Span( + "180%: ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span("32,000"), + IrcFormat.Span( + " | ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(2) + ) + ), + IrcFormat.Span( + "Pop: ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span("73819"), + ), + IrcFormatDeserializer.parse( + "\u000302New Break set to: \u000303Target: \u000399388 \u000302| \u000303Type: " + + "\u000399GS | \u000303Break: \u00039958,000 \u000302| \u000303120%: \u00039948,000 | " + + "\u000303135%: \u00039943,000 \u000302| \u000303145%: \u00039940,000 \u000302| " + + "\u000303180%: \u00039932,000\u000302 | \u000303Pop: \u00039973819\u000f" + ).toList() + ) + } + + @Test + fun testStrikethrough() { + assertEquals( + listOf( + IrcFormat.Span("Normal"), + IrcFormat.Span( + "Strikethrough", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.STRIKETHROUGH) + ) + ), + IrcFormat.Span("Normal") + ), + IrcFormatDeserializer.parse( + "Normal\u001eStrikethrough\u001eNormal" + ).toList() + ) + } + + @Test + fun testInverse() { + assertEquals( + listOf( + IrcFormat.Span("First"), + IrcFormat.Span( + "Second", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.INVERSE) + ) + ), + IrcFormat.Span( + "Red/Green", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.INVERSE), + foreground = IrcFormat.Color.Mirc(4), + background = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span( + "Green/Red", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4), + background = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span( + "Green/Magenta", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(6), + background = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span( + "Magenta/Green", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.INVERSE), + foreground = IrcFormat.Color.Mirc(6), + background = IrcFormat.Color.Mirc(3), + ) + ), + ), + IrcFormatDeserializer.parse( + "First\u0016Second\u00034,3Red/Green\u0016Green/Red\u00036Green/Magenta\u0016Magenta/Green" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span("First"), + IrcFormat.Span( + "Second", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.INVERSE) + ) + ), + IrcFormat.Span( + "Third", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.INVERSE), + foreground = IrcFormat.Color.Mirc(2) + ) + ), + IrcFormat.Span( + "Red/Green", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4), + background = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span( + "Green/Red", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.INVERSE), + foreground = IrcFormat.Color.Mirc(4), + background = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span( + "Green/Magenta", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.INVERSE), + foreground = IrcFormat.Color.Mirc(6), + background = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span( + "Magenta/Green", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(6), + background = IrcFormat.Color.Mirc(3), + ) + ), + ), + IrcFormatDeserializer.parse( + "First\u0012Second\u00032Third\u0012\u00034,3Red/Green\u0012Green/Red\u00036Green/Magenta\u0016Magenta/Green" + ).toList() + ) + } + + @Test + fun testMonospace() { + assertEquals( + listOf( + IrcFormat.Span( + "test ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + "test", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.MONOSPACE), + foreground = IrcFormat.Color.Mirc(4) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u00034test \u0011test" + ).toList() + ) + assertEquals( + listOf( + IrcFormat.Span( + "test ", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.MONOSPACE) + ) + ), + IrcFormat.Span( + "test", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.MONOSPACE), + foreground = IrcFormat.Color.Mirc(4) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u0011test \u00034test" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span("`test "), + IrcFormat.Span( + "test`", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ) + ), + IrcFormatDeserializer.parse( + "`test \u00034test`" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "[test ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + "nick`name", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4), + flags = setOf(IrcFormat.Flag.BOLD) + ) + ), + IrcFormat.Span( + "] [nick`name]", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u00034[test \u0002nick`name\u0002] [nick`name]" + ).toList() + ) + } + + @Test + fun testColors() { + assertEquals( + listOf( + IrcFormat.Span("Test 1: "), + IrcFormat.Span( + "[", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(12) + ) + ), + IrcFormat.Span( + "6,7,3,9,10,4,8,10,5", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(6) + ) + ), + IrcFormat.Span( + "]", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(12) + ) + ), + IrcFormat.Span(" "), + IrcFormat.Span( + "Test2: ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(14) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(14) + ) + ), + IrcFormat.Span( + "[", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(12) + ) + ), + IrcFormat.Span("2,9"), + IrcFormat.Span( + "]", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(12) + ) + ), + ), + IrcFormatDeserializer.parse( + "Test 1: \u0002\u000312[\u00036\u00026,7,3,9,10,4,8,10,5\u0002\u000312]" + + "\u0003\u0002 \u000314Test2: \u0002 \u000312[\u0003\u00022,9\u0002\u000312]\u0003\u0002" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "Extended colors", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(55), + background = IrcFormat.Color.Mirc(25) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u000355,25Extended colors\u0003" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "Transparent extended colors", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD, IrcFormat.Flag.UNDERLINE), + foreground = IrcFormat.Color.Mirc(55), + background = IrcFormat.Color.Mirc(25) + ) + ), + IrcFormat.Span( + " cleared fg", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD, IrcFormat.Flag.UNDERLINE), + background = IrcFormat.Color.Mirc(25) + ) + ), + IrcFormat.Span( + " cleared bg", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD, IrcFormat.Flag.UNDERLINE), + foreground = IrcFormat.Color.Mirc(55) + ) + ), + IrcFormat.Span( + " cleared both", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD, IrcFormat.Flag.UNDERLINE) + ) + ), + IrcFormat.Span( + " cleared bold", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.UNDERLINE) + ) + ), + IrcFormat.Span(" cleared all") + ), + IrcFormatDeserializer.parse( + "\u001f\u0002\u000355,25Transparent extended colors\u000399,25 cleared fg\u000355,99 cleared bg" + + "\u000399,99 cleared both\u0002 cleared bold\u000f cleared all", + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "Sniper_ShooterCZ", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(0), + background = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + "(1)", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(0), + background = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span(":"), + IrcFormat.Span( + " kokote", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(2) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u00030,1\u0002Sniper_ShooterCZ\u0002(1)\u000f:\u00032 kokote" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "uncurry", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(9) + ) + ), + IrcFormat.Span(" "), + IrcFormat.Span( + "Vect", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(12) + ) + ), + IrcFormat.Span(" : "), + IrcFormat.Span( + "(Nat,", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(12) + ) + ), + IrcFormat.Span(" "), + IrcFormat.Span( + "Type)", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(12) + ) + ), + IrcFormat.Span(" -> "), + IrcFormat.Span( + "Type", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(12) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u000309uncurry\u000f \u000312Vect\u000f : \u000312(\u000f\u000312Nat\u000f\u000312," + + "\u000f \u000312Type\u000f\u000312)\u000f -> \u000312Type\u000f" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span("*** ("), + IrcFormat.Span( + "ACTIVITIES", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD) + ) + ), + IrcFormat.Span("): Mugging: "), + IrcFormat.Span( + "||||||", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3), + background = IrcFormat.Color.Mirc(3), + ) + ), + IrcFormat.Span( + "||", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4), + background = IrcFormat.Color.Mirc(4), + ) + ), + IrcFormat.Span( + "34%", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(0), + background = IrcFormat.Color.Mirc(4), + ) + ), + IrcFormat.Span( + "||||||||", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4), + background = IrcFormat.Color.Mirc(4), + ) + ), + IrcFormat.Span(" | [under dev] Piracy: "), + IrcFormat.Span( + "|||||||", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4), + background = IrcFormat.Color.Mirc(4), + ) + ), + IrcFormat.Span( + "0.9%", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(0), + background = IrcFormat.Color.Mirc(4), + ) + ), + IrcFormat.Span( + "||||||||", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4), + background = IrcFormat.Color.Mirc(4), + ) + ), + IrcFormat.Span(" (exploring) | At this rate, you will get: Fined") + ), + IrcFormatDeserializer.parse( + "*** (\u0002ACTIVITIES\u000f): Mugging: \u000303,03|\u000303,03|\u000303,03|\u000303,03|" + + "\u000303,03|\u000303,03|\u000304,04|\u000304,04|\u000300,043\u000300,044\u000300,04%" + + "\u000304,04|\u000304,04|\u000304,04|\u000304,04|\u000304,04|\u000304,04|\u000304,04|" + + "\u000304,04|\u000300,04\u000f | [under dev] Piracy: \u000304,04|\u000304,04|" + + "\u000304,04|\u000304,04|\u000304,04|\u000304,04|\u000304,04|\u000300,040\u000300,04." + + "\u000300,049\u000300,04%\u000304,04|\u000304,04|\u000304,04|\u000304,04|\u000304,04|" + + "\u000304,04|\u000304,04|\u000304,04|\u000300,04\u000f (exploring) | At this rate, you " + + "will get: Fined", + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "\\u000308 ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(8) + ) + ), + IrcFormat.Span( + "\\u000310 ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(10) + ) + ), + IrcFormat.Span( + "\\u0002 ", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(10) + ) + ), + IrcFormat.Span( + "\\u000304 ", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + "\\u0002 ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + "\\u000309 ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(9) + ) + ), + IrcFormat.Span( + "\\u0002 ", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(9) + ) + ), + IrcFormat.Span( + "\\u0002", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(9) + ) + ), + ), + IrcFormatDeserializer.parse( + "\u000308\\u000308 \u000310\\u000310 \u0002\\u0002 \u000304\\u000304 \u0002\\u0002 " + + "\u000309\\u000309 \u0002\\u0002 \u0002\\u0002" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "teal", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(10) + ) + ), + IrcFormat.Span( + "boldteal", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(10) + ) + ), + IrcFormat.Span( + "boldred", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + "red", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u000310teal\u0002boldteal\u000304boldred\u0002red", + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "The channel for help with general IRC things such as ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span( + "clients", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(13) + ) + ), + IrcFormat.Span( + ", ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span( + "BNCs", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(7) + ) + ), + IrcFormat.Span( + ", ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span( + "bots", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + ", ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span( + "scripting", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(6) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(6) + ) + ), + IrcFormat.Span( + "etc.", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u00033The channel for help with general IRC things such as \u0002\u000313clients" + + "\u0002\u00033, \u0002\u00037BNCs\u0002\u00033, \u0002\u00034bots\u0002\u00033, " + + "\u0002\u00036scripting\u0002 \u00033etc.", + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "hi ", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(10) + ) + ), + IrcFormat.Span( + "hola", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(10) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u0002\u000310hi \u0002hola" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "hi ", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(10) + ) + ), + IrcFormat.Span( + "hola", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u000310\u0002hi \u0003hola" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "h", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(10) + ) + ), + IrcFormat.Span( + "i ", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + "hola", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u0002\u000310h\u00034i \u0002hola" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "__", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4), + background = IrcFormat.Color.Mirc(4), + ) + ), + IrcFormat.Span( + "(", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3), + background = IrcFormat.Color.Mirc(0), + ) + ), + IrcFormat.Span( + "✰", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(8), + background = IrcFormat.Color.Mirc(0), + ) + ), + IrcFormat.Span( + ")", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3), + background = IrcFormat.Color.Mirc(0), + ) + ), + IrcFormat.Span( + "__", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(2), + background = IrcFormat.Color.Mirc(2), + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(0), + background = IrcFormat.Color.Mirc(1), + ) + ), + IrcFormat.Span( + "Ejercito Paraguayo", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(0), + background = IrcFormat.Color.Mirc(1), + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(0), + background = IrcFormat.Color.Mirc(1), + ) + ), + IrcFormat.Span( + "__", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4), + background = IrcFormat.Color.Mirc(4), + ) + ), + IrcFormat.Span( + "(", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3), + background = IrcFormat.Color.Mirc(0), + ) + ), + IrcFormat.Span( + "✰", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(8), + background = IrcFormat.Color.Mirc(0), + ) + ), + IrcFormat.Span( + ")", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3), + background = IrcFormat.Color.Mirc(0), + ) + ), + IrcFormat.Span( + "__", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(2), + background = IrcFormat.Color.Mirc(2), + ) + ), + IrcFormat.Span("***** Lord Commander: mdmg - Sub-Comandantes: Sgto_Galleta ***** "), + IrcFormat.Span( + " Vencer o Morir!!! Que alguien pase una nueva xd", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(0), + background = IrcFormat.Color.Mirc(4), + ) + ), + IrcFormat.Span(" https://i.imgur.com/bTWzTuA.jpg"), + ), + IrcFormatDeserializer.parse( + "\u00034,4__\u00033,0(\u00038,0✰\u00033,0)\u00032,2__\u00030,1 \u0002Ejercito Paraguayo" + + "\u0002 \u00034,4__\u00033,0(\u00038,0✰\u00033,0)\u00032,2__" + + "\u00031\u0003***** Lord Commander: mdmg - Sub-Comandantes: Sgto_Galleta ***** " + + "\u00030,4 Vencer o Morir!!! Que alguien pase una nueva xd" + + "\u0003 https://i.imgur.com/bTWzTuA.jpg" + ).toList() + ) + + assertEquals( + emptyList(), + IrcFormatDeserializer.parse( + "\u00034\u000f" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span("hello") + ), + IrcFormatDeserializer.parse( + "\u00034\u000fhello" + ).toList() + ) + + assertEquals( + emptyList(), + IrcFormatDeserializer.parse( + "\u00031" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + ">bold", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span("test") + ), + IrcFormatDeserializer.parse( + "\u000304\u0002>bold\u0002\u0003test" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "P", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(7) + ) + ), + IrcFormat.Span("layers"), + IrcFormat.Span( + "(", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(7) + ) + ), + IrcFormat.Span( + "1/12", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(14) + ) + ), + IrcFormat.Span( + ")", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(7) + ) + ), + IrcFormat.Span(" "), + IrcFormat.Span( + "Kenzi", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(15) + ) + ), + IrcFormat.Span(" "), + IrcFormat.Span( + "C", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(7) + ) + ), + IrcFormat.Span("urrent votewinner: none | ?? help for all the commands | www.no1gaming.eu | #no1"), + ), + IrcFormatDeserializer.parse( + "\u00037P\u000flayers\u00037(\u0003141/12\u00037)\u000f \u000315Kenzi\u0003 \u00037C" + + "\u000furrent votewinner: none | ?? help for all the commands | www.no1gaming.eu | #no1", + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span("First "), + IrcFormat.Span( + "Red ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + "Green", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(3) + ) + ), + IrcFormat.Span( + " Bold", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD) + ) + ) + ), + IrcFormatDeserializer.parse( + "First \u00034Red \u00033Green\u0003\u0002 Bold\u0002\u000f", + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span("First "), + IrcFormat.Span( + "Color", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + " Bold", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD) + ) + ), + IrcFormat.Span(" unnecessary: "), + IrcFormat.Span( + "Color", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span("\u0000 plain "), + IrcFormat.Span( + "Color", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span(" "), + IrcFormat.Span( + "Bold", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD) + ) + ), + IrcFormat.Span(" "), + IrcFormat.Span( + "No space color New color", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + ), + IrcFormatDeserializer.parse( + "First \u00034Color\u0003\u0002 Bold\u0002 unnecessary:\u0003 \u00034Color" + + "\u0003\u0000 plain \u00034Color\u0003\u000f \u0002Bold\u000f \u00034No space color" + + "\u0003\u00034 New color\u000f", + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span("DALnet's recommended mIRC scripting & bot help channel. "), + IrcFormat.Span( + "Visit us at ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + "www.dalnethelpdesk.com", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.UNDERLINE), + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span(" for scripting info, forums, and searchable logs/stats "), + IrcFormat.Span( + "Looking for a script/bot/addon?", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(12) + ) + ), + IrcFormat.Span(" "), + IrcFormat.Span( + "mircscripts.org", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD, IrcFormat.Flag.UNDERLINE) + ) + ), + IrcFormat.Span(" "), + IrcFormat.Span( + "or", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span(" "), + IrcFormat.Span( + "mirc.net", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD, IrcFormat.Flag.UNDERLINE) + ) + ), + IrcFormat.Span(" "), + IrcFormat.Span( + " Writing your own?", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(12) + ) + ), + IrcFormat.Span( + " Ask ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + "here.", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span(" - "), + IrcFormat.Span( + "m", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(12) + ) + ), + IrcFormat.Span( + "IR", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + "Casdsaa", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(8) + ) + ), + IrcFormat.Span("asdasd v7.14 has been released") + ), + IrcFormatDeserializer.parse( + "DALnet's recommended mIRC scripting & bot help channel. \u00034Visit us at " + + "\u001fwww.dalnethelpdesk.com\u000f for \u0003scripting info, forums, and searchable " + + "logs/stats \u000312Looking for a script/bot/addon?\u000f \u0002\u001fmircscripts.org" + + "\u000f \u00034or\u000f \u0002\u001fmirc.net\u000f \u000312 Writing your own?\u0003" + + "\u00034 Ask \u0002here.\u0002 \u000f - \u000312m\u00034IR\u00038Casdsaa\u0003asdasd" + + "\u000f v7.14 has been released", + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span("i was last seen \\ \\"), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + "test^", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(7) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + "._", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(7) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + " '--' ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(14) + ) + ), + IrcFormat.Span( + "'-.\\__/ ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(12) + ) + ), + IrcFormat.Span( + "_", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(14) + ) + ), + IrcFormat.Span( + "l", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(12) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + "\\", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + "\\", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + "||", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(13) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + "/", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(7) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + "test", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD), foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + "^", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(7) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + ")", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(9) + ) + ), + IrcFormat.Span( + "\\", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(14) + ) + ), + IrcFormat.Span( + "((((", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(15) + ) + ), + IrcFormat.Span( + "\\", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(7) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + ".", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(7) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + " :;;,,", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(8) + ) + ), + IrcFormat.Span( + "'-._", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + IrcFormat.Span( + "\\", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4) + ) + ), + IrcFormat.Span( + " ", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(1) + ) + ), + ), + IrcFormatDeserializer.parse( + "i was last seen \\ \\\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 " + + "\u00031 \u00031 \u00031 \u00031 \u00031 \u00037test^\u00031 \u00031 \u00031 \u00031 " + + "\u00031 \u00031 \u00037._\u00031 \u00031 \u00031 \u000314 '--' \u000312'-.\\__/ " + + "\u000314_\u000312l\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00034\\" + + "\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00034\\\u00031 " + + "\u00031 \u00031 \u00031 \u00031 \u00031 \u000313||\u00031 \u00031 \u00031 \u00031 " + + "\u00031 \u00031 \u00031 \u00037/\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 " + + "\u00031 \u00034\u0002test\u0002\u00031 \u00031 \u00037^\u00031 \u00031 \u00031 " + + "\u00031 \u00039)\u000314\\\u000315((((\u00037\\\u00031 \u00031 \u00031 \u00037." + + "\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 " + + "\u00031 \u00031 \u00038 :;;,,\u00034'-._\u00031 \u00031 \u00034\\\u00031 \u00031 " + + "\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 " + + "\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 " + + "\u00031 \u00031 \u00031" + ).toList() + ) + } + + @Test + fun testHexColors() { + assertEquals( + listOf( + IrcFormat.Span( + "some text in 55ee22 rgb", + IrcFormat.Style( + foreground = IrcFormat.Color.Hex(0x55ee22) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u000455ee22some text in 55ee22 rgb\u0004" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + ",some text in 55ee22 rgb", + IrcFormat.Style( + foreground = IrcFormat.Color.Hex(0x55ee22) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u000455ee22,some text in 55ee22 rgb\u0004" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + "some text in 55ee22 rgb on aaaaaa bg", + IrcFormat.Style( + foreground = IrcFormat.Color.Hex(0x55ee22), + background = IrcFormat.Color.Hex(0xaaaaaa) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u000455ee22,aaaaaasome text in 55ee22 rgb on aaaaaa bg\u0004" + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + ",some text in 55ee22 rgb on aaaaaa bg", + IrcFormat.Style( + foreground = IrcFormat.Color.Hex(0x55ee22), + background = IrcFormat.Color.Hex(0xaaaaaa) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u000455ee22,aaaaaa,some text in 55ee22 rgb on aaaaaa bg\u0004", + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + ",some text in 55ee22 rgb on aaaaaa bg", + IrcFormat.Style( + foreground = IrcFormat.Color.Hex(0x55ee22), + background = IrcFormat.Color.Hex(0xaaaaaa) + ) + ), + IrcFormat.Span( + " Bold", + IrcFormat.Style( + flags = setOf(IrcFormat.Flag.BOLD) + ) + ) + ), + IrcFormatDeserializer.parse( + "\u000455ee22,aaaaaa,some text in 55ee22 rgb on aaaaaa bg\u0004\u0002 Bold\u0002", + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + ",some text in 55ee22 rgb on aaaaaa bg", + IrcFormat.Style( + foreground = IrcFormat.Color.Hex(0x55ee22), + background = IrcFormat.Color.Hex(0xaaaaaa) + ) + ), + IrcFormat.Span("\u0000") + ), + IrcFormatDeserializer.parse( + "\u000455ee22,aaaaaa,some text in 55ee22 rgb on aaaaaa bg\u0004\u0000", + ).toList() + ) + + assertEquals( + listOf( + IrcFormat.Span( + ",some text in 55ee22 rgb on aaaaaa bg", + IrcFormat.Style( + foreground = IrcFormat.Color.Hex(0x55ee22), + background = IrcFormat.Color.Hex(0xaaaaaa) + ) + ), + IrcFormat.Span( + " Red", + IrcFormat.Style( + foreground = IrcFormat.Color.Mirc(4), + ) + ) + ), + IrcFormatDeserializer.parse( + "\u000455ee22,aaaaaa,some text in 55ee22 rgb on aaaaaa bg\u0004\u00034 Red\u0003", + ).toList() + ) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 45551feff648941b1cc35b9c1383976e7f98ac65..c1a227a5a8bc789e1e38f8c8582a9d0dd6da0020 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,6 +6,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/. */ + enableFeaturePreview("VERSION_CATALOGS") rootProject.name = "libquassel" @@ -14,9 +15,10 @@ includeBuild("gradle/convention") include( ":libquassel-annotations", - ":libquassel-protocol", + ":libquassel-client", ":libquassel-generator", - ":libquassel-client" + ":libquassel-irc", + ":libquassel-protocol" ) pluginManagement {