2013-02-26 19 views
9

Napisałem funkcję Emacs Lisp, która wywołuje polecenie powłoki do przetwarza dany ciąg znaków i zwraca wynikowy ciąg znaków. Oto uproszczony przykład, który po prostu wywołuje tr do konwersji tekstu na wielkie litery:Jak zapobiec ustawianiu granicy cofania przez Emacs?

(defun test-shell-command (str) 
    "Apply tr to STR to convert lowercase letters to uppercase." 
    (let ((buffer (generate-new-buffer "*temp*"))) 
    (with-current-buffer buffer 
     (insert str) 
     (call-process-region (point-min) (point-max) "tr" t t nil "'a-z'" "'A-Z'") 
     (buffer-string)))) 

Funkcja ta tworzy tymczasowy bufor, wstawia tekst wzywa tr, zastępuje tekst z wyniku, i zwraca wynik.

Powyższa funkcja działa zgodnie z oczekiwaniami, jednak po napisaniu wrappera wokół tej funkcji, aby zastosować polecenie do regionu, do historii cofania dodawane są dwa kroki: . Oto kolejny przykład:

(defun test-shell-command-region (begin end) 
    "Apply tr to region from BEGIN to END." 
    (interactive "*r") 
    (insert (test-shell-command (delete-and-extract-region begin end)))) 

Kiedy zadzwonić M-x test-shell-command-on-region region otrzymuje z tekstu wielkimi literami, ale po naciśnięciu C-_ (undo), pierwszy krok w historii cofania jest stan z tekstem został usunięty . Przechodząc o kolejne dwa kroki, oryginalny tekst zostanie przywrócony.

Moje pytanie brzmi: w jaki sposób można zapobiec dodaniu kroku pośredniego z numeru do historii cofania? Czytałem Emacs documentation on undo, , ale nie wydaje się, aby rozwiązać ten problem, o ile wiem.

Oto funkcja, która realizuje to samo przez wywołanie wbudowanej funkcji Emacs upcase, podobnie jak poprzednio: na skutek delete-and-extract-region z wynikiem są przekazywane do insert:

(defun test-upcase-region (begin end) 
    "Apply upcase to region from BEGIN to END." 
    (interactive "*r") 
    (insert (upcase (delete-and-extract-region begin end)))) 

Podczas wywoływania M-x test-upcase-region jest tylko jeden krok w historii cofania , zgodnie z oczekiwaniami. Wydaje się więc, że wywołanie test-shell-command tworzy granicę cofania. Czy można tego w jakiś sposób uniknąć ?

+0

Zwykłym sposobem, aby zapobiec poleceń od zaśmiecania '(aś)' jest znaleźć inną drogę , który tego nie robi. Przykładem tutaj jest to, że prawie nigdy nie należy tworzyć tymczasowego bufora, lecz pracować z obiektami. – PascalVKooten

+0

Oprócz ręcznego czytania i zapisywania w plikach tymczasowych, nie jestem pewien inny sposób przechwytywania danych wyjściowych procesu. Nawet asynchroniczne polecenia procesowe, takie jak 'start-process', wydają się chcieć wysłać dane wyjściowe do bufora. –

+1

@JasonBlevins Piąty parametr polecenia 'shell-command-on-region' to REPLACE, dlaczego więc chcesz go zawinąć? –

Odpowiedz

6

Klucz jest nazwą bufora. Zobacz Maintaining Undo:

Nagrywanie cofniętych informacji w nowo utworzonym buforze jest zwykle włączone, aby rozpocząć; ale jeśli nazwa bufora zaczyna się od spacji, nagrywanie cofania jest początkowo wyłączone. Możesz jawnie włączyć lub wyłączyć cofanie nagrywania za pomocą poniższych dwóch funkcji lub poprzez samodzielne ustawianie listy cofania.

with-temp-buffer tworzy bufor o nazwie ␣*temp* (zauważ wiodącą spacje), natomiast czynność korzysta *temp*.

Aby usunąć granicę cofania w kodzie, użyj nazwy bufora z początkową spacją lub jawnie wyłącz cofanie przekodowania w buforze tymczasowym z buffer-disable-undo.

Ale generalnie, używaj naprawdę with-temp-buffer. Jest to standardowy sposób na takie rzeczy w Emacs, dzięki czemu twój zamiar jest jasny dla każdego, kto czyta twój kod. Ponadto, with-temp-buffer stara się prawidłowo wyczyścić bufor tymczasowy.


Jak, dlaczego cofnąć w buforze czasowym tworzy granicę cofania w bieżącym jednym: Jeśli poprzednia zmiana była cofnąć i wykonane w innym buforze (tymczasowy jeden w tym przypadku), niejawna granica jest tworzona . Od undo-boundary:

Wszystkie modyfikacje buforowe dodać granicę gdy poprzednia cofnąć zmiany został wykonany w jakimś innego bufora. Ma to na celu zapewnienie, że każde polecenie tworzy granicę w każdym buforze, w którym wprowadza zmiany.

Stąd hamowanie cofnąć w buforze czasowym usuwa granicę cofania w bieżącym buforze też: Poprzednia zmiana nie jest już po prostu cofnąć, a tym samym nie niejawna granica jest tworzony.

+1

Sprawdziłem, czy przy użyciu '(generate-new-buffer" * temp * ")' w oryginalnej funkcji działa i że 'with-temp-buffer' rzeczywiście tworzy bufor, którego nazwa ma wiodącą przestrzeń. Przeczytałem tę część dokumentacji, ale nie sądziłem, że to był problem, ponieważ cofanie informacji jest specyficzne dla każdego bufora. Dlaczego powinienem się przejmować, czy cofnięto informacje w buforze '* temp *'? Ale nie udało mi się zebrać z dokumentacji, że najwyraźniej akt zapisu cofania informacji w drugim buforze musi ustawić granicę cofania w bieżącym buforze. –

+0

@JasonBlevins Utworzono niejawną granicę, jeśli poprzednia zmiana była nieodwracalna i wykonana w innym buforze, w twoim przypadku tymczasowym. Zmieniłem swoją odpowiedź, aby wyjaśnić szczegóły. Mam nadzieję, że teraz wszystko jest jasne. – lunaryorn

2

roztworu, w tym przypadku jest stworzenie tymczasowego buforu wyjściowego pomocą with-temp-buffer zamiast jawnie tworzyć jeden z generate-new-buffer. Poniższy alternatywna wersja pierwszej funkcji nie tworzy granicę cofania:

(defun test-shell-command (str) 
    "Apply tr to STR to convert lowercase letters to uppercase." 
    (with-temp-buffer 
    (insert str) 
    (call-process-region (point-min) (point-max) "tr" t t nil "'a-z'" "'A-Z'") 
    (buffer-string))) 

I nie był w stanie określić, czy jest rzeczywiście generate-new-buffer tworząc granicę cofania, ale ten problem został rozwiązany. generate-new-buffer połączenia get-buffer-create, które jest zdefiniowane w kodu źródłowego C, ale nie mogłem szybko określić, co było dzieje się w kategoriach historii cofania.

Podejrzewam, że problem może być związany z następującym fragmencie w Emacs Lisp Manual entry for undo-boundary:

Wszystkie modyfikacje buforowe dodać granicę, gdy poprzednia cofnąć zmiana została wprowadzona w inny bufor.Ma to na celu zapewnienie, że każde polecenie tworzy granicę w każdym buforze, w którym zmienia się .

Nawet with-temp-buffer wywołuje makro generate-new-buffer niż w pierwotnej funkcji, dokumentacja dla with-temp-buffer stwierdza, że ​​nie ma cofania informacje są zapisywane (nawet chociaż nie ma nic w źródle Emacs Lisp, że sugeruje to byłoby być przypadek):

domyślnie Undo (Cofnij zobaczyć) nie są zapisywane w buforze stworzonej przez to makro (ale ciało może umożliwić, że w razie potrzeby).

3

Istnieje wiele sytuacji, w których używanie tymczasowego bufora nie jest praktyczne. Ciężko jest debugować, co dzieje się na przykład.

W tych przypadkach można pozwolić, wiążą undo-inhibit-record-point aby zapobiec Emacs z podjęciem decyzji, gdzie umieścić granice:

(let ((undo-inhibit-record-point t)) 
    ;; Then record boundaries manually 
    (undo-boundary) 
    (do-lots-of-stuff) 
    (undo-boundary)) 
Powiązane problemy