2013-02-26 30 views
9

Tôi đã viết một hàm Emacs Lisp gọi lệnh shell để xử lý một chuỗi đã cho và trả về chuỗi kết quả. Đây là một đơn giản hóa ví dụ mà chỉ gọi tr để chuyển đổi văn bản thành chữ hoa:Làm thế nào để ngăn chặn Emacs thiết lập một ranh giới hoàn tác?

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

Chức năng này tạo ra một bộ đệm tạm thời, chèn văn bản, gọi tr, thay thế văn bản với kết quả, và trả về kết quả.

Hàm trên hoạt động như mong đợi, khi tôi viết trình bao bọc xung quanh chức năng này để áp dụng lệnh cho khu vực, hai bước là đang được thêm vào lịch sử hoàn tác. Dưới đây là một ví dụ khác:

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

Khi tôi gọi M-x test-shell-command-on-region, khu vực này được thay thế với văn bản chữ hoa, nhưng khi tôi nhấn C-_ (undo), các bước đầu tiên trong lịch sử undo là trạng thái với các văn bản đã xóa . Di chuyển hai bước trở lại, văn bản gốc được khôi phục.

Câu hỏi của tôi là, làm cách nào để ngăn chặn bước trung gian bị thêm vào lịch sử hoàn tác? Tôi đã đọc số Emacs documentation on undo, nhưng dường như nó không giải quyết được điều này.

Dưới đây là một chức năng để thực hiện chức điều tương tự bằng cách gọi built-in Emacs chức năng upcase, giống như trước: trên kết quả của delete-and-extract-region với kết quả được đưa ra để 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)))) 

Khi gọi M-x test-upcase-region, chỉ có một bước trong lịch sử hoàn tác , như mong đợi. Vì vậy, có vẻ như trường hợp gọi test-shell-command sẽ tạo một đường biên hoàn tác. Điều đó có thể tránh được bằng cách nào đó không?

+0

Cách thông thường để ngăn chặn các lệnh từ cluttering '(undo)' là phải tìm một cách khác nhau một cái không làm điều đó. Ví dụ ở đây là bạn hầu như không bao giờ nên tạo một bộ đệm tạm thời, nhưng thay vào đó làm việc với các đối tượng. – PascalVKooten

+0

Khác với việc đọc và ghi thủ công vào các tệp tạm thời, tôi không chắc chắn về một cách khác để nắm bắt đầu ra quy trình. Ngay cả các lệnh xử lý không đồng bộ như 'start-process' dường như muốn gửi đầu ra tới một bộ đệm. –

+1

@JasonBlevins Tham số thứ năm cho 'shell-command-on-region' là REPLACE, vậy tại sao bạn cần bọc nó? –

Trả lời

6

Khóa là tên đệm. Xem Maintaining Undo:

Ghi lại thông tin hoàn tác trong bộ đệm mới tạo thường được bật để bắt đầu; nhưng nếu tên đệm bắt đầu bằng dấu cách, ghi hoàn tác ban đầu bị tắt. Bạn có thể bật hoặc tắt hoàn tác ghi âm một cách rõ ràng với hai chức năng sau, hoặc bằng cách tự cài đặt bộ đệm-hoàn tác danh sách.

with-temp-buffer tạo ra một bộ đệm tên ␣*temp* (chú ý khoảng trắng hàng đầu), trong khi chức năng của bạn sử dụng *temp*.

Để xóa ranh giới hoàn tác trong mã của bạn, hãy sử dụng tên bộ đệm có không gian phía trước hoặc vô hiệu hóa hoàn tác việc khôi phục trong bộ đệm tạm thời với buffer-disable-undo.

Nhưng nhìn chung, hãy sử dụng with-temp-buffer. Đó là cách tiêu chuẩn cho những điều như vậy trong Emacs, làm cho ý định của bạn rõ ràng cho bất kỳ ai đọc mã của bạn. Ngoài ra, with-temp-buffer cố gắng hết sức để xóa bộ đệm tạm thời một cách chính xác.


Còn về lý do lùi lại trong bộ đệm tạm thời tạo ra một ranh giới undo trong mục hiện thời: Nếu sự thay đổi trước đây là không thể nén và thực hiện trong một số bộ đệm khác (tạm thời một trong trường hợp này), một ranh giới ngầm được tạo ra . Từ undo-boundary:

Tất cả những thay đổi đệm thêm một ranh giới bất cứ khi nào trước thay đổi undoable đã được thực hiện ở một số đệm khác. Điều này là để đảm bảo rằng mỗi lệnh tạo một đường biên trong mỗi vùng đệm, nơi nó tạo ra các thay đổi.

Do đó, việc ngăn cản hoàn tác trong bộ đệm tạm thời sẽ xóa bỏ ranh giới hoàn tác trong bộ đệm hiện tại: Thay đổi trước đó không thể hoàn tác được nữa và do đó không tạo ra ranh giới ngầm.

+1

Tôi đã xác minh rằng sử dụng '(generate-new-buffer" * temp * ")' trong hàm ban đầu hoạt động và 'with-temp-buffer' thực sự tạo ra một bộ đệm có tên có một không gian hàng đầu. Tôi đọc phần đó của tài liệu, nhưng tôi không nghĩ đó là vấn đề vì hoàn tác thông tin cụ thể cho từng bộ đệm. Tại sao tôi nên quan tâm xem thông tin hoàn tác có được ghi trong bộ đệm '* temp *' không? Nhưng những gì tôi không thu thập được từ tài liệu là, rõ ràng, hành vi ghi lại thông tin hoàn tác trong bộ đệm khác phải thiết lập một ranh giới hoàn tác trong bộ đệm hiện tại. –

+0

@JasonBlevins Một ranh giới ngầm được tạo ra, nếu thay đổi trước đó là không thể hoàn tác và được thực hiện trong một số bộ đệm khác, trong trường hợp của bạn là tạm thời. Tôi đã chỉnh sửa câu trả lời của mình để giải thích chi tiết. Hy vọng mọi thứ đã rõ ràng. – lunaryorn

2

Giải pháp trong trường hợp này là tạo ra sản lượng đệm tạm thời sử dụng with-temp-buffer, thay vì tạo ra một cách rõ ràng với generate-new-buffer. Phiên bản thay thế sau của hàm đầu tiên không tạo ra một ranh giới lùi lại:

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

tôi đã không thể để xác định xem generate-new-buffer thực sự là tạo ranh giới lùi lại, nhưng điều này khắc phục vấn đề. generate-new-buffer gọi get-buffer-create, được xác định trong mã nguồn C, nhưng tôi không thể nhanh chóng xác định được những gì là xảy ra theo lịch sử hoàn tác.

tôi nghi ngờ rằng vấn đề này có thể liên quan đến việc thông qua sau trong các Emacs Lisp Manual entry for undo-boundary:

Tất cả những thay đổi đệm thêm một ranh giới bất cứ khi nào thay đổi không thể nén trước đó đã được thực hiện trong một số bộ đệm khác.Điều này là để đảm bảo rằng mỗi lệnh tạo một đường biên trong mỗi vùng đệm nơi nó thực hiện các thay đổi .

Mặc dù with-temp-buffer cuộc gọi vĩ mô generate-new-buffer nhiều càng tốt trong chức năng ban đầu, tài liệu cho with-temp-buffer khẳng định rằng không có thông tin lùi lại được lưu (thậm chí mặc dù không có gì trong nguồn Emacs Lisp cho thấy này là sẽ là trường hợp):

theo mặc định, undo (xem Undo) không được ghi lại trong bộ đệm tạo ra bởi macro này (nhưng cơ thể có thể kích hoạt tính năng đó, nếu cần thiết).

3

Có nhiều trường hợp sử dụng bộ đệm tạm thời là không thực tế. Thật khó để gỡ lỗi những gì đang xảy ra chẳng hạn.

Trong những trường hợp này bạn có thể cho-bind undo-inhibit-record-point để ngăn chặn Emacs từ quyết định nơi để đặt ranh giới:

(let ((undo-inhibit-record-point t)) 
    ;; Then record boundaries manually 
    (undo-boundary) 
    (do-lots-of-stuff) 
    (undo-boundary)) 
Các vấn đề liên quan