TL;DR
我有替换文本、字符串和光标位置(数字)的函数,如果字符串的长度发生变化,则需要为使用replace函数创建的新字符串得到更正的位置(数字):
input and cursor position: foo ba|r text
replacement: foo -> baz_text, bar -> quux_text
result: baz_text qu|ux_text text
input and cursor position: foo bar| text
replacement: foo -> baz_text, bar -> quux_text
result: baz_text quux_text| text
input and cursor position: foo bar| text
replacement: foo -> f, bar -> b
result: f b| text
input and cursor position: foo b|ar text
replacement: foo -> f, bar -> b
result: f b| text问题是,我可以在原始文本上使用子字符串,但是替换将不匹配整个单词,所以需要对整个文本进行替换,但是子字符串将不匹配替换。
我对解决方案也很满意,当原始游标位于被替换词的中间时,游标总是位于单词的末尾。
现在,我的实现,在jQuery终端中,我有一个格式化程序函数数组:
$.terminal.defaults.formatters它们接受一个字符串,并且它应该返回新的字符串,它可以很好地工作,但这种情况除外:
如果中断命令行,则当格式化程序更改长度时,例如,此格式化程序:
$.terminal.defaults.formatters.push(function(string) {
return string.replace(/:smile:/g, 'a')
.replace(/(foo|bar|baz)/g, 'text_$1');
});当命令行获得新字符串时,光标位置出错。
我试着解决这个问题,但是它不像预期的那样工作,终端的内部是这样的,
当我更改position时,我会在命令行中使用另一个变量formatted_position来显示游标。为了得到这个值,我使用如下:
formatted_position = position;
var string = formatting(command);
var len = $.terminal.length(string);
var command_len = $.terminal.length(command);
if (len !== command_len) {
var orig_sub = $.terminal.substring(command, 0, position);
var orig_len = $.terminal.length(orig_sub);
var formatted = formatting(orig_sub);
var formatted_len = $.terminal.length(formatted);
if (orig_len > formatted_len) {
// if formatting make substring - (text before cursor)
// shorter then subtract the difference
formatted_position -= orig_len - formatted_len;
} else if (orig_len < formatted_len) {
// if the formatted string is longer add difference
formatted_position += formatted_len - orig_len;
}
}
if (formatted_position > len) {
formatted_position = len;
} else if (formatted_position < 0) {
formatted_position = 0;
}$.terminal.substring和$.terminal.length是具有终端格式化功能的辅助函数(类似于此[[b;#fff;]hello]的文本),如果要编写解决方案,可以使用普通文本和字符串方法。
问题是,当我将光标移动到被更改的单词的中间时
当文本较长时,它是一种工作,但对于较短的字符串,当文本位于被替换的单词的中间时,光标会跳到右边。
我也尝试使用以下代码来修复这个问题:
function find_diff(callback) {
var start = position === 0 ? 0 : position - 1;
for (var i = start; i < command_len; ++i) {
var substr = $.terminal.substring(command, 0, i);
var next_substr = $.terminal.substring(command, 0, i + 1);
var formatted = formatting(next_substr);
var substr_len = $.terminal.length(substr);
var formatted_len = $.terminal.length(formatted);
var diff = Math.abs(substr_len - formatted_len);
if (diff > 1) {
return diff;
}
}
return 0;
}
...
} else if (len < command_len) {
formatted_position -= find_diff();
} else if (len > command_len) {
formatted_position += find_diff();
}但我认为这会使情况更糟,因为当光标在替换词的前面或中间时,它会发现差异,而只有在替换词的中间时,它才能找到差异。
您可以看到我在这个代码页https://codepen.io/jcubic/pen/qPVMPg?editors=0110中尝试的结果(允许输入emoji和foo baz被text_$1替换)。
更新
我用这段代码做了一些工作:
// ---------------------------------------------------------------------
// :: functions used to calculate position of cursor when formatting
// :: change length of output text like with emoji demo
// ---------------------------------------------------------------------
function split(formatted, normal) {
function longer(str) {
return found && length(str) > length(found) || !found;
}
var formatted_len = $.terminal.length(formatted);
var normal_len = $.terminal.length(normal);
var found;
for (var i = normal_len; i > 1; i--) {
var test_normal = $.terminal.substring(normal, 0, i);
var formatted_normal = formatting(test_normal);
for (var j = formatted_len; j > 1; j--) {
var test_formatted = $.terminal.substring(formatted, 0, j);
if (test_formatted === formatted_normal &&
longer(test_normal)) {
found = test_normal;
}
}
}
return found || '';
}
// ---------------------------------------------------------------------
// :: return index after next word that got replaced by formatting
// :: and change length of text
// ---------------------------------------------------------------------
function index_after_formatting(position) {
var start = position === 0 ? 0 : position - 1;
var command_len = $.terminal.length(command);
for (var i = start; i < command_len; ++i) {
var substr = $.terminal.substring(command, 0, i);
var next_substr = $.terminal.substring(command, 0, i + 1);
var formatted_substr = formatting(substr);
var formatted_next = formatting(next_substr);
var substr_len = length(formatted_substr);
var next_len = length(formatted_next);
var test_diff = Math.abs(next_len - substr_len);
if (test_diff > 1) {
return i;
}
}
}
// ---------------------------------------------------------------------
// :: main function that return corrected cursor position on display
// :: if cursor is in the middle of the word that is shorter the before
// :: applying formatting then the corrected position is after the word
// :: so it stay in place when you move real cursor in the middle
// :: of the word
// ---------------------------------------------------------------------
function get_formatted_position(position) {
var formatted_position = position;
var string = formatting(command);
var len = $.terminal.length(string);
var command_len = $.terminal.length(command);
if (len !== command_len) {
var orig_sub = $.terminal.substring(command, 0, position);
var orig_len = $.terminal.length(orig_sub);
var sub = formatting(orig_sub);
var sub_len = $.terminal.length(sub);
var diff = Math.abs(orig_len - sub_len);
if (false && orig_len > sub_len) {
formatted_position -= diff;
} else if (false && orig_len < sub_len) {
formatted_position += diff;
} else {
var index = index_after_formatting(position);
var to_end = $.terminal.substring(command, 0, index + 1);
//formatted_position -= length(to_end) - orig_len;
formatted_position -= orig_len - sub_len;
if (orig_sub && orig_sub !== to_end) {
var formatted_to_end = formatting(to_end);
var common = split(formatted_to_end, orig_sub);
var re = new RegExp('^' + $.terminal.escape_regex(common));
var to_end_rest = to_end.replace(re, '');
var to_end_rest_len = length(formatting(to_end_rest));
if (common orig_sub !== common) {
var commnon_len = length(formatting(common));
formatted_position = commnon_len + to_end_rest_len;
}
}
}
if (formatted_position > len) {
formatted_position = len;
} else if (formatted_position < 0) {
formatted_position = 0;
}
}
return formatted_position;
}当你把表情符号作为第一个字符而光标位于:微笑:单词的中间时,这种情况就不适用了。如何修复get_formatted_position函数在更换后有正确的定位?
UPDATE:我问了不同的简单问题,并使用接受regex和string的trackingReplace函数获得了解决方案,因此我将格式化程序的API更改为接受带有regex和替换后正确的子字符串位置函数字符串的数组。
发布于 2017-10-12 04:35:38
因此,我能够完成给定的任务,但是我无法将它实现到库中,因为我不知道如何在那里实现许多东西。
我是用vanilla编写的,所以在库中实现时不应该有任何问题。脚本主要依赖于textarea、input或类似元素上可用的selectionStart和selectionEnd属性。在完成所有替换之后,使用setSelectionRange方法将新的选择设置为textarea。
// sel = [selectionStart, selectionEnd]
function updateSelection(sel, replaceStart, oldLength, newLength){
var orig = sel.map(a => a)
var diff = newLength - oldLength
var replaceEnd = replaceStart + oldLength
if(replaceEnd <= sel[0]){
// Replacement occurs before selection
sel[0] += diff
sel[1] += diff
console.log('Replacement occurs before selection', orig, sel)
}else if(replaceStart <= sel[0]){
// Replacement starts before selection
if(replaceEnd >= sel[1]){
// and ends after selection
sel[1] += diff
}else{
// and ends in selection
}
console.log('Replacement starts before selection', orig, sel)
}else if(replaceStart <= sel[1]){
// Replacement starts in selection
if(replaceEnd < sel[1]){
// and ends in seledtion
}else{
// and ends after selection
sel[1] += diff
}
console.log('Replacement starts in selection', orig, sel)
}
}下面是完整的演示:码页。
PS:根据我的观察,格式脚本运行到经常。
https://stackoverflow.com/questions/46592643
复制相似问题