yank-pop ¶yank と current-kill
を理解したことで、yank-pop
関数にどう取り組めばよいかが分かったはずです。スペース節約のためドキュメントは省略しますが、内容は以下の通りです。
(defun yank-pop (&optional arg)
"..."
(interactive "*p")
(if (not (eq last-command 'yank))
(error "Previous command was not a yank"))
(setq this-command 'yank)
(unless arg (setq arg 1))
(let ((inhibit-read-only t)
(before (< (point) (mark t))))
(if before
(funcall (or yank-undo-function 'delete-region) (point) (mark t))
(funcall (or yank-undo-function 'delete-region) (mark t) (point)))
(setq yank-undo-function nil)
(set-marker (mark-marker) (point) (current-buffer))
(insert-for-yank (current-kill arg))
;; Set the window start back where it was in the yank command,
;; if possible.
(set-window-start (selected-window) yank-window-start t)
(if before
;; This is like exchange-point-and-mark,
;; but doesn't activate the mark.
;; It is cleaner to avoid activation, even though the command
;; loop would deactivate the mark because we inserted text.
(goto-char (prog1 (mark t)
(set-marker (mark-marker)
(point)
(current-buffer))))))
nil)
この関数は小さな ‘p’
でインタラクティブに動作するため、プレフィックス引数が処理され、関数に渡されます。このコマンドは、事前にヤンクを行った後でなければ使用できず、それ以外の場合はエラーメッセージが送信されます。このチェックでは、yank によって設定され、別の箇所で説明されている変数 last-command が使用されます。(copy-region-as-kill を参照してください。)
let 節は、point が mark より前か後かに応じて変数 before に true または false を設定し、その後 point と mark
の間の領域を削除します。これは直前の yank によって挿入されたばかりの領域であり、置換対象となるのはこのテキストです。
funcall
は第1引数を関数として呼び出し、残りの引数をその関数に渡します。第1引数は、or
式が返す値です。残りの2つの引数は、直前の yank コマンドで設定された point と mark
の位置です。
他にもあるが、そこが一番難しいところだ。