Keep the account ID visual transformation length-preserving.

This commit is contained in:
Greyson Parrelli
2026-09-02 16:11:30 -03:00
committed by Alex Hart
parent 4408ab3386
commit e3ff228684
3 changed files with 37 additions and 3 deletions
@@ -16,14 +16,17 @@ internal object AccountIdFormat {
/** Offsets in a raw account ID that a dash is inserted in front of. */
private val DASH_OFFSETS = intArrayOf(8, 12, 16, 20)
/** Rewrites a raw account ID with the dashes a UUID is normally written with. */
/**
* Rewrites a raw account ID with the dashes and uppercasing a UUID is normally written with.
*/
fun dashed(accountId: String): String {
return buildString {
for ((index, character) in accountId.withIndex()) {
if (index in DASH_OFFSETS) {
append('-')
}
append(character)
// Uppercase on a per-character basis so as to not screw up offsets
append(character.uppercaseChar())
}
}
}
@@ -470,7 +470,7 @@ internal object AccountIdVisualTransformation : VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
return TransformedText(
text = AnnotatedString(AccountIdFormat.dashed(text.text).uppercase()),
text = AnnotatedString(AccountIdFormat.dashed(text.text)),
offsetMapping = AccountIdOffsetMapping(text.length)
)
}
@@ -15,6 +15,9 @@ class AccountIdVisualTransformationTest {
companion object {
private const val FULL_KEY = "a6b284822e3283d07f2391360a4c2b91"
private const val FULL_KEY_FORMATTED = "A6B28482-2E32-83D0-7F23-91360A4C2B91"
/** Characters whose whole-string uppercase mapping is longer than the character itself. */
private val LENGTH_CHANGING_CHARACTERS = listOf('ß', 'ʼn', 'ff', 'fi', 'ǰ')
}
@Test
@@ -54,5 +57,33 @@ class AccountIdVisualTransformationTest {
}
}
@Test
fun `characters that uppercase into multiple characters stay one character long`() {
for (character in LENGTH_CHANGING_CHARACTERS) {
assertThat(transform(character.toString()).length).isEqualTo(1)
assertThat(transform("a6b2848$character")).isEqualTo("A6B2848${character.uppercaseChar()}")
}
}
@Test
fun `every cursor position round trips even when the key holds characters that normally expand`() {
for (character in LENGTH_CHANGING_CHARACTERS) {
val key = character + FULL_KEY.drop(1)
val mapping = AccountIdVisualTransformation.filter(AnnotatedString(key)).offsetMapping
val transformedLength = transform(key).length
for (offset in 0..key.length) {
val transformed = mapping.originalToTransformed(offset)
assertThat(transformed in 0..transformedLength).isEqualTo(true)
assertThat(mapping.transformedToOriginal(transformed)).isEqualTo(offset)
}
for (offset in 0..transformedLength) {
assertThat(mapping.transformedToOriginal(offset) in 0..key.length).isEqualTo(true)
}
}
}
private fun transform(text: String): String = AccountIdVisualTransformation.filter(AnnotatedString(text)).text.text
}