음성 인식으로 Emacs를 제어 할 수있는 Emacs 모드에서 작업하고 있습니다. 내가 겪은 문제 중 하나는 Emacs가 실행 취소를 처리하는 방식이 음성으로 제어 할 때 예상대로 작동하지 않는다는 것입니다.
사용자가 여러 단어를 말한 다음 일시 중지하면이를 '발언'이라고합니다. 발화는 Emacs가 실행하는 여러 명령으로 구성 될 수 있습니다. 인식기가 발화 내에서 하나 이상의 명령을 잘못 인식하는 경우가 종종 있습니다. 그 시점에서 나는 "실행 취소"라고 말하고 이맥스 가 발언 내의 마지막 행동 만이 아니라 발언에 의해 수행 된 모든 행동을 취소하게하고 싶다 . 즉, 발언이 여러 명령으로 구성된 경우에도 이맥스가 발언을 실행 취소와 관련하여 단일 명령으로 취급하기를 원합니다. 또한 발언 전의 정확한 위치로 돌아가고 싶습니다. 정상적인 Emacs 실행 취소 가이 작업을 수행하지 않는 것을 알았습니다.
각 발언의 시작과 끝에서 콜백을 받도록 Emacs를 설정 했으므로 상황을 감지 할 수 있습니다. Emacs가 무엇을해야하는지 파악해야합니다. 이상적으로는 (undo-start-collapsing)
다음 (undo-stop-collapsing)
과 같이 호출 하고 그 사이에 수행 된 모든 작업은 마술처럼 하나의 레코드로 축소됩니다.
나는 문서를 통해 트롤링을하고 발견 undo-boundary
했지만, 내가 원하는 것과 반대입니다. 발언 내의 모든 동작을 하나의 실행 취소 레코드로 축소하고 분할하지 않아야합니다. undo-boundary
발화 사이 를 사용 하여 삽입이 개별적으로 간주되도록 할 수 있습니다 (기본적으로 이맥스는 연속적인 삽입 동작을 최대 한도까지 고려합니다).
다른 합병증 :
- 내 음성 인식 데몬은 X11의 키 누름을 시뮬레이션하여 이맥스에 몇 가지 명령을 보내고 통해 일부를 전송
emacsclient -e
말이 있다면, 그래서(undo-collapse &rest ACTIONS)
내가 포장 수있는 중앙 곳이 없습니다. - 나는
undo-tree
이것이 더 복잡한 지 확실하지 않습니다. 이상적인 솔루션은undo-tree
Emacs의 정상적인 실행 취소 동작과 함께 작동하는 것 입니다. - 발언 내 명령 중 하나가 "실행 취소"또는 "재실행"이면 어떻게됩니까? 콜백 로직을 변경하여 항상 간단하게 유지하기 위해 별도의 발언으로 Emacs에 보낼 수 있다고 생각합니다. 그러면 키보드를 사용하는 것처럼 처리해야합니다.
- 스트레치 목표 : 발화에는 현재 활성화 된 창 또는 버퍼를 전환하는 명령이 포함될 수 있습니다. 이 경우 각 버퍼에서 개별적으로 "실행 취소"라고 한 번하는 것이 좋습니다. 그렇게 멋질 필요는 없습니다. 그러나 단일 버퍼의 모든 명령은 여전히 그룹화되어야하므로 "do-x do-y do-z 스위치 버퍼 do-a do-b do-c"라고하면 x, y, z는 실행 취소해야합니다. 원래 버퍼에 기록하고 a, b, c는 전환 된 버퍼에있는 하나의 기록이어야합니다.
이 작업을 수행하는 쉬운 방법이 있습니까? AFAICT는 내장되어 있지 않지만 Emacs는 광대하고 깊습니다 ...
업데이트 : 나는 약간의 추가 코드로 아래 jhc의 솔루션을 사용했습니다. 전역 before-change-hook
에서 변경되는 버퍼 가이 발언을 수정 한 전체 버퍼 목록에 있는지 확인합니다. 목록에 들어 가지 않으면 undo-collapse-begin
호출됩니다. 그런 다음 발언이 끝나면 목록의 모든 버퍼를 반복하고 호출 undo-collapse-end
합니다. 아래 코드 (이름 공간 지정을 위해 함수 이름 앞에 md- 추가됨) :
(defvar md-utterance-changed-buffers nil)
(defvar-local md-collapse-undo-marker nil)
(defun md-undo-collapse-begin (marker)
"Mark the beginning of a collapsible undo block.
This must be followed with a call to undo-collapse-end with a marker
eq to this one.
Taken from jch's stackoverflow answer here:
http://emacs.stackexchange.com/a/7560/2301
"
(push marker buffer-undo-list))
(defun md-undo-collapse-end (marker)
"Collapse undo history until a matching marker.
Taken from jch's stackoverflow answer here:
http://emacs.stackexchange.com/a/7560/2301"
(cond
((eq (car buffer-undo-list) marker)
(setq buffer-undo-list (cdr buffer-undo-list)))
(t
(let ((l buffer-undo-list))
(while (not (eq (cadr l) marker))
(cond
((null (cdr l))
(error "md-undo-collapse-end with no matching marker"))
((eq (cadr l) nil)
(setf (cdr l) (cddr l)))
(t (setq l (cdr l)))))
;; remove the marker
(setf (cdr l) (cddr l))))))
(defmacro md-with-undo-collapse (&rest body)
"Execute body, then collapse any resulting undo boundaries.
Taken from jch's stackoverflow answer here:
http://emacs.stackexchange.com/a/7560/2301"
(declare (indent 0))
(let ((marker (list 'apply 'identity nil)) ; build a fresh list
(buffer-var (make-symbol "buffer")))
`(let ((,buffer-var (current-buffer)))
(unwind-protect
(progn
(md-undo-collapse-begin ',marker)
,@body)
(with-current-buffer ,buffer-var
(md-undo-collapse-end ',marker))))))
(defun md-check-undo-before-change (beg end)
"When a modification is detected, we push the current buffer
onto a list of buffers modified this utterance."
(unless (or
;; undo itself causes buffer modifications, we
;; don't want to trigger on those
undo-in-progress
;; we only collapse utterances, not general actions
(not md-in-utterance)
;; ignore undo disabled buffers
(eq buffer-undo-list t)
;; ignore read only buffers
buffer-read-only
;; ignore buffers we already marked
(memq (current-buffer) md-utterance-changed-buffers)
;; ignore buffers that have been killed
(not (buffer-name)))
(push (current-buffer) md-utterance-changed-buffers)
(setq md-collapse-undo-marker (list 'apply 'identity nil))
(undo-boundary)
(md-undo-collapse-begin md-collapse-undo-marker)))
(defun md-pre-utterance-undo-setup ()
(setq md-utterance-changed-buffers nil)
(setq md-collapse-undo-marker nil))
(defun md-post-utterance-collapse-undo ()
(unwind-protect
(dolist (i md-utterance-changed-buffers)
;; killed buffers have a name of nil, no point
;; in undoing those
(when (buffer-name i)
(with-current-buffer i
(condition-case nil
(md-undo-collapse-end md-collapse-undo-marker)
(error (message "Couldn't undo in buffer %S" i))))))
(setq md-utterance-changed-buffers nil)
(setq md-collapse-undo-marker nil)))
(defun md-force-collapse-undo ()
"Forces undo history to collapse, we invoke when the user is
trying to do an undo command so the undo itself is not collapsed."
(when (memq (current-buffer) md-utterance-changed-buffers)
(md-undo-collapse-end md-collapse-undo-marker)
(setq md-utterance-changed-buffers (delq (current-buffer) md-utterance-changed-buffers))))
(defun md-resume-collapse-after-undo ()
"After the 'undo' part of the utterance has passed, we still want to
collapse anything that comes after."
(when md-in-utterance
(md-check-undo-before-change nil nil)))
(defun md-enable-utterance-undo ()
(setq md-utterance-changed-buffers nil)
(when (featurep 'undo-tree)
(advice-add #'md-force-collapse-undo :before #'undo-tree-undo)
(advice-add #'md-resume-collapse-after-undo :after #'undo-tree-undo)
(advice-add #'md-force-collapse-undo :before #'undo-tree-redo)
(advice-add #'md-resume-collapse-after-undo :after #'undo-tree-redo))
(advice-add #'md-force-collapse-undo :before #'undo)
(advice-add #'md-resume-collapse-after-undo :after #'undo)
(add-hook 'before-change-functions #'md-check-undo-before-change)
(add-hook 'md-start-utterance-hooks #'md-pre-utterance-undo-setup)
(add-hook 'md-end-utterance-hooks #'md-post-utterance-collapse-undo))
(defun md-disable-utterance-undo ()
;;(md-force-collapse-undo)
(when (featurep 'undo-tree)
(advice-remove #'md-force-collapse-undo :before #'undo-tree-undo)
(advice-remove #'md-resume-collapse-after-undo :after #'undo-tree-undo)
(advice-remove #'md-force-collapse-undo :before #'undo-tree-redo)
(advice-remove #'md-resume-collapse-after-undo :after #'undo-tree-redo))
(advice-remove #'md-force-collapse-undo :before #'undo)
(advice-remove #'md-resume-collapse-after-undo :after #'undo)
(remove-hook 'before-change-functions #'md-check-undo-before-change)
(remove-hook 'md-start-utterance-hooks #'md-pre-utterance-undo-setup)
(remove-hook 'md-end-utterance-hooks #'md-post-utterance-collapse-undo))
(md-enable-utterance-undo)
;; (md-disable-utterance-undo)
buffer-undo-list
마커 로 삽입 할 수 있습니다 . 양식의 항목(apply FUN-NAME . ARGS)
입니까? 그런 다음 발화를 취소하려면undo
다음 마커를 찾을 때까지 반복해서 전화를 겁니다 . 그러나 나는 여기에 모든 종류의 합병증이 있다고 생각합니다. :)