2017-10-05 81 views
7

TL; DRMecz pozycja kursora na podciągu po tekście zastąpić

Mam funkcję zamiany tekstu, położenie strun i kursora (numer) i muszę dostać skorygowaną pozycję (numer) do nowego łańcucha, który jest stworzony z funkcji wymienić jeśli długość jeżeli zmiana strun:

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 

problemem jest to, że można używać podciąg na oryginalnym tekście, lecz następnie zastąpienie nie będzie pasował całe słowo, więc muszą być wykonane dla całego tekstu, ale potem podłańcuch nie będzie pasował do zamiennika.

Nie mam też nic przeciwko rozwiązaniu, w którym kursor znajduje się zawsze na końcu słowa, gdy oryginalny kursor znajduje się w środku zastąpionego słowa.

i teraz moja realizacja, w jQuery Terminal Mam tablicę funkcji formatujących w:

$.terminal.defaults.formatters 

przyjmują ciąg i powinien on powrócić nowy ciąg działać dobrze z wyjątkiem tym przypadku:

kiedy Mam formatter, który zmienia długość, jeśli przerwie linię poleceń, na przykład ten formatator:

$.terminal.defaults.formatters.push(function(string) { 
    return string.replace(/:smile:/g, 'a') 
       .replace(/(foo|bar|baz)/g, 'text_$1'); 
}); 

wtedy pozycja kursora była błędna, gdy polecenie li ne otrzymasz nowy ciąg.

ja staram się to naprawić, ale to nie działa zgodnie z oczekiwaniami, wewnętrzna terminalu wyglądać tak,

przy zmianie position mam skrzynie kolejna zmienna formatted_position to użycie w wierszu poleceń wyświetl kursor. aby ta wartość używam to:

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

$ i $ .terminal.length .terminal.substring są pomocnicze funkcje, które są świadome formatowania zacisk (tekst, który wygląda tak [[b;#fff;]hello]), jeśli będziesz pisać rozwiązanie można użyć normalny tekst i użycie metod łańcuchowych.

problemem jest to, że kiedy przesunąć kursor w środku wyrazu, który zmienił

to niby pracy, gdy tekst jest dłuższy, ale na krótszy ciąg skoku kursora w prawo, gdy tekst jest w środek słowa, które zostało zastąpione.

ja staram się to naprawić, jak również przy użyciu tego kodu:

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

ale myślę uczynić go jeszcze gorzej ponieważ posiadał znaleźć diff, gdy kursor znajduje się przed lub w środku zastąpiła słowem i powinno Znajdź różnicę tylko wtedy, gdy kursor znajduje się w środku zastąpionego słowa.

można zobaczyć wynik z moich prób w tym codepen https://codepen.io/jcubic/pen/qPVMPg?editors=0110 (które pozwalają wpisać emoji i foo bar baz zastępowane przez text_$1)

UPDATE:

mam zrobić to rodzaj pracować z tym kodem:

// --------------------------------------------------------------------- 
    // :: 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; 
    } 

to nie działa na jednym przypadku po wpisaniu emotikonów jako pierwszego znaku, a kursor znajduje się w środku: smile: słowo. Jak naprawić funkcję get_formatted_position, aby mieć poprawną, stałą pozycję po zamianie?

UPDATE: mam poprosić innego i proste pytanie i mam rozwiązanie przy użyciu funkcji trackingReplace że akceptują regex i ciąg, więc mam zmienić API dla formatek zaakceptować tablicę z regex i ciąg wzdłuż funkcji Correct substring position after replacement

+1

Czy jesteś pewien, że przesunięcie kursora na zastąpione słowo jest prawidłowe? Dla mnie wydaje się to dość mylące. Zajrzałbym do Worda lub do google docs, jeśli nie poruszą go na początku ani na końcu. – Akxe

+0

@Akxe to zupełnie inny przypadek niż słowo lub google docs, ponieważ tekst jest zastępowany podczas wpisywania nie, gdy zastępujesz tekst, jak w funkcji wyszukiwania/zamiany. I wygląda to bardzo dziwnie, gdy nie zmieniasz pozycji, ponieważ możesz być końcem końca tekstu, który ma długość 10, a zastąpiony tekst jest równy 3, a otrzymasz pozycję 10, która powinna wynosić 3. – jcubic

+0

Miałem na myśli, jeśli zamień słowo, w którym znajduje się twój kursor, wtedy lepiej jest umieścić kursor z przodu lub po nowym zastąpionym słowie. A Word na pewno ma opcję do zastąpienia. – Akxe

Odpowiedz

1

Tak więc udało mi się wykonać dane zadanie, jednak nie byłem w stanie wprowadzić go do biblioteki, ponieważ nie jestem pewien, jak zaimplementować wiele rzeczy.

Zrobiłem to w javascript wanilii, więc nie powinno być żadnych problemów podczas implementacji do biblioteki. Skrypt jest w większości zależny od właściwości selectionStart i selectionEnd dostępnych na textarea, input lub podobnych elementach. Po dokonaniu wymiany nowa opcja zostanie ustawiona na tekst za pomocą metody setSelectionRange.

// 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) 
    } 
} 

Oto cała wersja demo: codepen.

PS: Z moich obserwacji skrypt formatowania często przechodzi.

+0

Przepraszam, może nie byłem wystarczająco jasny, ale nie chcę utrzymywać kursora dla zaznaczonego tekstu w textarea, ale potrzebuję tego dla normalnego łańcucha znaków (nie mam żadnej selekcji), a kursor jest po prostu liczbą nie prawdziwym kursorem textarea. – jcubic

+0

Mam textarea w terminalu, ale musi mieć oryginalny tekst, a nie zastąpiony, i jeszcze jedno Mam funkcję, jak już wspomniałem, że wszystkie zamienniki nie mogę iterować na klucze jak w kodzie, a także jedną funkcję wymiany może rób wiele słów, a nie jeden, aby twój kod się zepsuł, kiedy robisz to 'string = string.replace (nowy RegExp (toRelpace.replace (/ ([()])/g, '\\ $ 1'), 'g') , replaceWith) ' – jcubic

+0

Potrzebujesz zatem zarówno sformatowanego tekstu, jak i oryginału? Wybór może być w większości niezmieniony, nawet jeśli nie używasz początku i końca, wyobraź sobie, że początek i koniec to ten sam numer. A regexps, masz je oddzielnie, po prostu dołącz je do kodu. – Akxe