You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建CR XXXX格式VisualTransformation遇偏移映射异常求助

问题描述

我需要实现一个支持长度参数的VisualTransformation,输入格式为CR XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX。当前代码在手动输入时表现正常,但编辑开头数字时,光标偏移映射出现错误。

现有代码

class InputAccountMaskTransformation(private val len: Int = 20) : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {
        val transformed = buildAnnotatedString {
            append("CR ")
            append(
                insertStringFromEnd(
                    text.text,
                    4,
                    " ",
                    stringMaxLength = len,
                    fillEmptyWith = "-",
                )
            )
        }

        return TransformedText(
            text = transformed,
            offsetMapping = object : OffsetMapping {
                override fun originalToTransformed(offset: Int): Int {
                    val transformedSplit = transformed.split(" ")
                    var increment = 2 + (transformedSplit.count() - 1)
                    if (text.text.isNotBlank()) {
                        transformedSplit.forEach { if (it.contains("----")) increment-- }
                    }
                    return offset.plus(increment)
                }

                override fun transformedToOriginal(offset: Int): Int {
                    var decrement = 2
                    if (text.text.isNotBlank()) {
                        for (c in transformed) {
                            if (c == '-' || c == ' ') {
                                decrement++
                            }
                        }
                    }
                    val transform = offset.minus(decrement)
                    val offsetValue = if (transform > 0 && transform < transformed.count()) {
                        transform
                    } else {
                        if (transform > (transformed.count() / 2)) {
                            transformed.count()
                        } else {
                            0
                        }
                    }
                    return offsetValue
                }
            }
        )
    }
}

现象说明

  • 手动输入时表现正常:
    手动输入正常
  • 编辑开头数字时光标偏移错误:
    编辑开头偏移错误
解决方案

问题出在OffsetMapping的实现上,当前逻辑采用固定增量/减量计算偏移,没有考虑光标在不同位置时的动态偏移。正确的做法是逐字符映射,分别处理原始文本到转换后文本、转换后文本到原始文本的位置对应关系。

修复后的代码如下:

class InputAccountMaskTransformation(private val len: Int = 20) : VisualTransformation {
    // 固定前缀
    private val prefix = "CR "
    // 每段字符长度
    private val segmentLength = 4
    // 段分隔符
    private val separator = " "

    override fun filter(text: AnnotatedString): TransformedText {
        val cleanedText = text.text.take(len)
        val transformed = buildAnnotatedString {
            append(prefix)
            // 构建分段格式的输入内容
            for (i in cleanedText.indices) {
                if (i != 0 && i % segmentLength == 0) {
                    append(separator)
                }
                append(cleanedText[i])
            }
            // 填充剩余的占位符
            val remaining = len - cleanedText.length
            if (remaining > 0) {
                val fullSegments = remaining / segmentLength
                val partialSegment = remaining % segmentLength
                for (i in 0 until fullSegments) {
                    if (cleanedText.isNotEmpty() || i != 0) {
                        append(separator)
                    }
                    append("----")
                }
                if (partialSegment > 0) {
                    if (cleanedText.isNotEmpty() || fullSegments > 0) {
                        append(separator)
                    }
                    append("-".repeat(partialSegment))
                }
            }
        }

        return TransformedText(transformed, object : OffsetMapping {
            override fun originalToTransformed(offset: Int): Int {
                if (offset == 0) return prefix.length
                // 计算原始位置对应的转换后位置:前缀长度 + 字符数 + 已添加的分隔符数量
                val separatorsCount = offset / segmentLength
                return prefix.length + offset + separatorsCount
            }

            override fun transformedToOriginal(offset: Int): Int {
                if (offset <= prefix.length) return 0
                // 去除前缀长度的影响
                val adjustedOffset = offset - prefix.length
                // 计算该位置前的分隔符数量
                val separatorsCount = adjustedOffset / (segmentLength + 1)
                // 还原原始位置,限制在合法范围内
                val originalOffset = adjustedOffset - separatorsCount
                return originalOffset.coerceIn(0, cleanedText.length)
            }
        })
    }
}

修复说明

  1. 原始到转换的映射(originalToTransformed):

    • 光标在原始文本的第offset位时,转换后的位置需加上前缀长度,再叠加该位置前的分隔符数量(每4个字符添加一个空格),确保光标对应到转换后文本的正确位置。
  2. 转换到原始的映射(transformedToOriginal):

    • 先减去前缀长度得到转换后文本的有效内容位置,再减去该位置前的分隔符数量,还原出原始文本的对应位置。
    • 通过coerceIn方法限制位置范围,避免光标越界。

修改后,无论在文本开头、中间还是末尾编辑,光标都能正确对应到原始文本的位置,解决偏移错误问题。


内容的提问来源于stack exchange,提问作者Fernando Sanchez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 05:16:00