2017-10-05 84 views
7

TL; DR子字符串匹配光標位置的文本替換後

我有函數替換文本,字符串和光標的位置(數字),我需要得到修正後的位置(數字)的新字符串,它是與替換功能創建如果長度字符串是否變化:

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'); 
}); 

然後將光標定位錯了,當李命令ne得到新的字符串。

我已經嘗試解決此問題,但如預期,內部終端這個樣子的吧不工作,

當我改變 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(); 
} 

但我認爲讓事情變得更糟,原因是其找到差異時光標之前或更換字的中間,它應該僅當光標位於替換字詞的中間才能找到diff。

你可以看到我的努力的結果,在這個codepen https://codepen.io/jcubic/pen/qPVMPg?editors=0110(即允許輸入表情符號和Foo酒吧巴茲得到由text_$1代替)

UPDATE

我讓它有種與此代碼的工作:微笑:字

// --------------------------------------------------------------------- 
    // :: 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函數在替換後具有正確的固定位置?

更新:我問不同的,簡單的問題,並使用正則表達式接受和字符串trackingReplace功能得到了解決,所以我改變了API,用於格式化接受與正則表達式和字符串一同功能陣列Correct substring position after replacement

+1

你確定,將光標移動到被替換的單詞是正確的嗎?對我來說,這似乎很混亂。如果他們不在開頭或結尾移動它,我會查看Word或Google文檔。 – Akxe

+0

@Akxe它是完全不同的情況比單詞或谷歌文檔,因爲文本被替換時,而不是當你做文字替換時搜索/替換功能。當你不改變位置時,它看起來很奇怪,因爲你可以結束長度爲10的文本的末尾,被替換的文本是3,並且你得到的位置是10,它應該是3. – jcubic

+0

我的意思是,如果你替換光標當前所在的單詞,然後將光標放在前面或在新的替換單詞之後可能會更好。而Word肯定有替代選項。 – Akxe

回答

1

所以我能夠完成給定的任務,但是我無法將其實現到庫中,因爲我不知道如何在那裏實現許多事情。

我在香草javascript中製作,所以在執行到圖書館時不應該有任何打嗝。該腳本主要依賴textarea,輸入或類似元素上可用的selectionStartselectionEnd屬性。全部替換完成後,使用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) 
    } 
} 

這裏是整個演示:codepen。 PS:從我的觀察,格式腳本經常運行的方式。

+0

對不起,也許我還不夠清楚,但我不想保留textarea中的選定文本的光標,但我需要這個用於普通字符串(我沒有任何選擇),光標只是一個數字而不是真正的textarea光標。 – jcubic

+0

我在終端textarea,但它需要有原始文本不是替換的一個,還有一件事我有一個函數,因爲我提到做所有替代我不能迭代像在你的代碼中的鍵,也可以替換一個函數做多個單詞不是單一的,所以當你這樣做的時候你的代碼會被破壞'string = string.replace(new RegExp(toRelpace.replace(/([()])/ g,'\\ $ 1'),'g') ,replaceWith)' – jcubic

+0

所以你需要格式化的文本以及原文?即使你不使用開始和結束,選擇仍然可以大部分不變,只要想象開始和結束是相同的數字。而正則表達式,你有他們分開,你只是在你的代碼中連接它們。 – Akxe