2015-07-21 13 views
9

redaguje:VIM Undo: Dlaczego kursor przeskakuje do niewłaściwej pozycji po cofnięciu "cofania"?


Dlaczego kursor umieszczony w różny sposób w dwóch poniższych przykładach :

  1. [PRAWID¸OWE pozycja kursora] Następujący testy wytwarza oczekiwane zmiany podstawienie wynik połączony z poprzedniej zmiany w buforze (dodanie linia 3), pozycja kursor prawidłowo doprowadzony do drugiej linii w buforze .

    normal ggiline one is full of aaaa 
    set undolevels=10 " splits the change into separate undo blocks 
    
    normal Goline two is full of bbbb 
    set undolevels=10 
    
    normal Goline three is full of cccc 
    set undolevels=10 
    
    undojoin 
    keepjumps %s/aaaa/zzzz/ 
    normal u 
    
  2. [NIEPOPRAWNA pozycja kursora] Następujący testy wytwarza nieoczekiwany wynik: Zmiana Zmiana jest połączona z poprzedniej zmiany w buforze (dodanie linia 4), pozycja kursor nieprawidłowo przywrócić pierwszy linia w buforze (powinna być linią 3).

    normal ggiline one is bull of aaaa 
    set undolevels=10 " splits the change into separate undo blocks 
    
    normal Goline two is full of bbbb 
    set undolevels=10 
    
    normal Goline three is full of cccc   
    set undolevels=10 
    
    normal Goline four is full of aaaa's again 
    set undolevels=10 
    
    undojoin 
    keepjumps %s/aaaa/zzzz/ 
    normal u 
    

oryginalne pytanie

Droga moja VIM jest skonfigurowana, oszczędzając bufor do pliku wyzwala zwyczaj StripTrailingSpaces() funkcja (załączony na końcu pytanie):

autocmd BufWritePre,FileWritePre,FileAppendPre,FilterWritePre <buffer> 
     \ :keepjumps call UmkaDK#StripTrailingSpaces(0) 

Po obejrzeniu Restore the cursor position after undoing text change made by a script, mam pomysł, aby wykluczyć zmiany dokonane przez moich StripTrailingSpaces() funkcja z historii cofania poprzez scalenie rekordu cofnięcia utworzonego przez funkcję na końcu poprzedniej zmiany w buforze.

W ten sposób, po cofnięciu zmian, wygląda na to, że funkcja nie utworzyła w ogóle własnego rekordu cofania.

Aby potwierdzić mój pomysł Użyłem prosty przypadek testowy: tworzenie czystego bufor i wprowadź następujące komendy ręcznie lub zapisać następujący blok jako plik i źródła, poprzez:

vim +"source <saved-filename-here>"

normal ggiline one is full of aaaa 
set undolevels=10 " splits the change into separate undo blocks 

normal Goline two is full of bbbb 
set undolevels=10 

normal Goline three is full of cccc 
set undolevels=10 

undojoin 
keepjumps %s/aaaa/zzzz/ 
normal u 

Jak widać, po cofnięciu ostatniej zmiany w buforze, która tworzy trzecią linię, kursor jest poprawnie zwracany do drugiej linii w pliku.

Od kiedy mój test zadziałał, zaimplementowałem prawie identyczne undojoin w moim StripTrailingSpaces(). Jednak po cofnięciu ostatniej zmiany po uruchomieniu funkcji kursor jest zwracany do najwyższej, najbardziej zmieniającej się w pliku. Często jest to spakowana przestrzeń i jest to pozycja, której zmiana została zmieniona na .

Czy ktoś może pomyśleć, dlaczego tak się stało? Jeszcze lepiej, czy ktoś może zaproponować poprawkę?

function! UmkaDK#StripTrailingSpaces(number_of_allowed_spaces) 
    " Match all trailing spaces in a file 
    let l:regex = [ 
       \ '\^\zs\s\{1,\}\$', 
       \ '\S\s\{' . a:number_of_allowed_spaces . '\}\zs\s\{1,\}\$', 
       \ ] 

    " Join trailing spaces regex into a single, non-magic string 
    let l:regex_str = '\V\(' . join(l:regex, '\|') . '\)' 

    " Save current window state 
    let l:[email protected]/ 
    let l:winview = winsaveview() 

    try 
     " Append the comming change onto the end of the previous change 
     " NOTE: Fails if previous change doesn't exist 
     undojoin 
    catch 
    endtry 

    " Substitute all trailing spaces 
    if v:version > 704 || v:version == 704 && has('patch155') 
     execute 'keepjumps keeppatterns %s/' . l:regex_str . '//e' 
    else 
     execute 'keepjumps %s/' . l:regex_str . '//e' 
     call histdel('search', -1) 
    endif 

    " Restore current window state 
    call winrestview(l:winview) 
    let @/=l:last_search 
endfunction 
+0

Niestety, ale jaka jest różnica między tymi 75 liniami a ':% s/\ s * $ /'? – steffen

+0

@steffen: Cóż ... jej 74 linii i 2866 znaków dłużej ... ma również opisowe komentarze, zachowuje historię wyszukiwania i ostatni ciąg wyszukiwania, nie zmienia znaków "", '', .' i ''^", nie dodaje nowego rekordu 'jumplist' i' changelist', zachowuje swój widok i pozycję kursora, a * powinien * tworzyć lepsze cofanie. (Chociaż ostatni punkt jest subiektywny i jest powodem, dla którego to pytanie jest tutaj.) – UmkaDK

+0

Pozycja kursora jest pamiętana przed wprowadzeniem zmian, a następnie przywracana po cofnięciu zmian. –

Odpowiedz

3

To zdecydowanie wygląda jak błąd z poleceniem zastępczym dla mnie. Z tego, co mogę powiedzieć, polecenie zastępcze będzie sporadycznie przejmować lokalizację zmiany, aby przejść do bloku cofania, gdy zostanie uwzględnione. Nie mogę wyizolować wzorca - czasem zrobi to, gdy zastępowanie nastąpi> kilka razy. Innym razem wydaje się, że lokalizacja substytucji ma wpływ, kiedy tak się dzieje. Wydaje się być bardzo niewiarygodny. Nie sądzę, że ma to coś wspólnego z poleceniem undojoin, ponieważ udało mi się odtworzyć ten efekt dla innych funkcji, które go nie wykorzystują. Jeśli jesteś zainteresowany, wypróbuj poniższe:

function! Test() 
    normal ciwfoo 
    normal ciwbar 
    %s/one/two/ 
endfunction 

Wypróbuj kilka różnych tekstów z różnymi numerami "załączników" dołączonych i umieszczonych w różnych lokalizacjach. Zauważysz, że potem czasami cofanie przeskoczy do linii, w której nastąpiło pierwsze zastąpienie, a innym razem przeskoczy do miejsca, w którym pierwsze normalne polecenie wprowadza zmianę.

myślę tu rozwiązaniem dla Ciebie będzie zrobić coś takiego:

undo 
normal ma 
redo 

na szczycie swojej funkcji, a następnie wiążą u do czegoś podobnego u'a w swojej funkcji tak, że po Cofnij, cofnie się do miejsca, w którym nastąpiła faktyczna pierwsza zmiana, w przeciwieństwie do sił losowości. Oczywiście nie może to być takie proste, ponieważ będziesz musiał odinstalować cię, gdy wykonasz swój skok itp., Ale ten wzór ogólnie powinien dać ci sposób na zapisanie poprawnej lokalizacji, a następnie cofnięcie się. do tego. Oczywiście, prawdopodobnie będziesz chciał zrobić to wszystko z pewną globalną zmienną zamiast z porwań, ale masz pomysł.

EDIT: Po spędzeniu pewnego czasu na przeszukiwaniu kodu źródłowego, wygląda na to, że zachowanie, którego szukasz, jest błędem. Jest to fragment kodu, który określa, gdzie kursor powinien być umieszczony po cofania:

if (top < newlnum) 
{ 
    /* If the saved cursor is somewhere in this undo block, move it to 
    * the remembered position. Makes "gwap" put the cursor back 
    * where it was. */ 
    lnum = curhead->uh_cursor.lnum; 
    if (lnum >= top && lnum <= top + newsize + 1) 
    { 
    MSG("Remembered Position.\n"); 
    curwin->w_cursor = curhead->uh_cursor; 
    newlnum = curwin->w_cursor.lnum - 1; 
    } 
    else 
    { 
    char msg_buf[1000]; 
    MSG("First change\n"); 
    sprintf(msg_buf, "lnum: %d, top: %d, newsize: %d", lnum, top, newsize); 
    MSG(msg_buf); 
    /* Use the first line that actually changed. Avoids that 
    * undoing auto-formatting puts the cursor in the previous 
    * line. */ 
    for (i = 0; i < newsize && i < oldsize; ++i) 
     if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0) 
     break; 
    if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL) 
    { 
     newlnum = top; 
     curwin->w_cursor.lnum = newlnum + 1; 
    } 
    else if (i < newsize) 
    { 
     newlnum = top + i; 
     curwin->w_cursor.lnum = newlnum + 1; 
    } 
    } 
} 

To raczej zaangażować, ale w zasadzie co to robi to sprawdzić, gdzie był kursor gdy zmiana została wykonana, a następnie, jeśli jest wewnątrz zmień blok dla cofania, a następnie zresetuj kursor do tej pozycji dla polecenia gw. W przeciwnym razie przechodzi do najbardziej zmienionej linii i umieszcza Cię tam. To, co dzieje się z substytutem, oznacza, że ​​aktywuje tę logikę dla każdej linii, która jest podstawiona, a więc jeśli jedno z tych podstawników znajduje się w bloku cofania, to przeskakuje do pozycji kursora przed cofnięciem (pożądane zachowanie). Innym razem żadna zmiana nie będzie w tym bloku, więc przeskoczy do najwyżej zmienionej linii (prawdopodobnie to, co powinna zrobić). Dlatego myślę, że odpowiedź na twoje pytanie jest taka, że ​​twoje pożądane zachowanie (dokonać zmiany, ale połączyć ją z poprzednią zmianą, z wyjątkiem ustalania, gdzie umieścić kursor po cofnięciu zmiany) nie jest obecnie obsługiwane przez vim.

EDYCJA: Ta konkretna porcja kodu znajduje się w undo.c na linii 2711 wewnątrz funkcji undoredo.Wewnątrz u_savecommon jest miejsce, w którym cała konfiguracja zostaje ustawiona zanim faktycznie zostanie wywołane cofnięcie i tam zapisywana jest pozycja kursora, która kończy się w przypadku wyjątku polecenia gw (linia undo.c 385 i zapisana w linii 548 po wywołaniu synchronizowanego bufora). Logika dla komendy substytucji znajduje się w ex_cmds.c na linii 4268, która wywołuje u_savecommon pośrednio w linii 5208 (wywołuje u_savesub, który wywołuje u_savecommon).

+0

Dzięki @doliver, wygląda na to, że poświęciłeś sporo czasu na debugowanie tego. Bardzo doceniane !! Jednak proponowane rozwiązanie (np. Ponowne przypisanie 'u' do niestandardowej akcji) wymaga modyfikacji jednej z podstawowych funkcji VIM, a ja wolałbym tego uniknąć. Także jeśli to zachowanie jest wynikiem błędu, proponowane rozwiązanie rozwiąże problem tylko dla mnie, a nie dla całej społeczności. Pozwól mi zobaczyć, czy mogę uzyskać #vim (IRC) i vim_dev (lista mailingowa) zaangażowanych w to. Może będą w stanie nam pomóc. – UmkaDK

+0

To pytanie jest teraz przeksięgowane na liście mailingowej vim_dev: https://goo.gl/HpW4NX – UmkaDK

+1

Tak, myślę, że idealnie byłoby to naprawione wewnątrz rdzenia. Nie sądzę jednak, że uzyskasz najwyższy priorytet w takim przypadku. Jeśli czujesz się komfortowo z C, nie zdziwiłbym się, gdyby to można było naprawić dość prosto. Oprócz robienia tego lub ponownego przypisywania klawisza u prawdopodobnie najlepsze obejście polega na tym, aby po prostu nie wykonywać funkcji cofania, która wymagałaby dodatkowego naciśnięcia klawisza, ale zachować tę pozycję. Zaczynałem już w vimie, więc mogę dziś spojrzeć na jakiś punkt i sprawdzić, czy to łatwa łatka. – doliver

Powiązane problemy