QML中TextEdit/TextArea格式化与非格式化文本索引映射方案咨询
QML TextEdit/TextArea 格式化与非格式化文本索引映射实现方案
核心实现思路
核心是预构建双向索引映射表,避免实时计算的性能损耗,单次生成后可反复查询,单次查询时间复杂度为O(1)。
- 两个映射数组:
formatToRaw:下标为格式化文本的字符索引,存储对应非格式化源码的字符索引rawToFormat:下标为非格式化源码的字符索引,存储对应格式化文本的字符索引,无对应位置的格式标记字符存为-1
- 生成逻辑:逐字符遍历非格式化源码,识别并跳过所有格式标记(HTML标签/Markdown语法符),累计偏移量,遇到普通可见字符时同步填充两个映射表的对应位置。
针对性场景处理
换行映射
针对格式化视图单换行对应源码双换行的场景,遍历源码时如果遇到连续两个\n,仅给格式化索引计数加1,源码索引累加2即可。
特殊标签处理
自闭合HTML标签(如<br/>、<img/>)单独适配规则:
<br/>对应格式化文本1个换行,直接建立双向映射<img/>对应格式化文本1个占位位置,按单个可见字符处理映射
示例代码(HTML场景)
// 文本更新时调用,生成双向映射表 function buildHtmlIndexMap(rawText) { const formatToRaw = [] const rawToFormat = new Array(rawText.length).fill(-1) let rawIdx = 0 let formatIdx = 0 const textLength = rawText.length while (rawIdx < textLength) { // 跳过HTML标签 if (rawText[rawIdx] === '<') { const tagEndPos = rawText.indexOf('>', rawIdx) if (tagEndPos === -1) break // 异常标签截断容错 const tagContent = rawText.slice(rawIdx, tagEndPos + 1) // 单独处理br换行标签 if (tagContent === '<br/>') { formatToRaw[formatIdx] = rawIdx rawToFormat[rawIdx] = formatIdx formatIdx++ } rawIdx = tagEndPos + 1 continue } // 普通可见字符,填充双向映射 formatToRaw[formatIdx] = rawIdx rawToFormat[rawIdx] = formatIdx rawIdx++ formatIdx++ } return { formatToRaw, rawToFormat } } // 使用示例:格式化视图选中内容转源码位置 function getRawSelection(formatStart, formatEnd, map) { return { rawStart: map.formatToRaw[formatStart] ?? 0, rawEnd: map.formatToRaw[formatEnd] ?? map.formatToRaw.at(-1) } }
优化建议
如果你的场景是大文本且频繁局部修改,不需要全量重新生成映射表,仅更新修改区间对应的映射关系即可,可将单次更新耗时从O(n)降到O(k),k为修改区间的字符长度。
如果是Markdown格式场景,仅需要把上述代码中的HTML标签识别逻辑替换为Markdown语法符识别即可,比如识别**、# 、>等格式标记,跳过对应长度的字符即可。
内容的提问来源于stack exchange,提问作者curiousJorgeXX
相关产品推荐
相关产品推荐

