yank-pop
After understanding yank
and current-kill
, you know how to
approach the yank-pop
function. Leaving out the documentation to
save space, it looks like this:
(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)
The function is interactive with a small ‘p’ so the prefix argument is
processed and passed to the function. The command can only be used after a
previous yank; otherwise an error message is sent. This check uses the
variable last-command
which is set by yank
and is discussed
elsewhere. (See copy-region-as-kill.)
The let
clause sets the variable before
to true or false
depending whether point is before or after mark and then the region between
point and mark is deleted. This is the region that was just inserted by the
previous yank and it is this text that will be replaced.
funcall
calls its first argument as a function, passing remaining
arguments to it. The first argument is whatever the or
expression
returns. The two remaining arguments are the positions of point and mark
set by the preceding yank
command.
There is more, but that is the hardest part.