Index: NEWS =================================================================== RCS file: /cvsroot/emacs/emacs/etc/NEWS,v retrieving revision 1.661 diff -u -u -r1.661 NEWS --- NEWS 28 Apr 2002 21:16:32 -0000 1.661 +++ NEWS 29 Apr 2002 04:30:49 -0000 @@ -49,6 +48,13 @@ * Changes in Emacs 21.4 + ++++ +** The key `M-y' has been extended to display a menu of kill ring +entries, if the previous command was not a 'C-y'. Previously, `M-y' +ran the command `yank-pop'. Now it runs `yank-previous', which will +either display a menu (`kill-ring-menu') or call `yank-pop'. See the +node "Yanking Earlier Kills" in the Emacs manual for more information. --- ** The new options `buffers-menu-show-directories' and --- killing.texi.~1.26.~ Wed Sep 12 17:01:34 2001 +++ killing.texi Sun Apr 28 16:25:50 2002 @@ -270,8 +270,7 @@ @item C-y Yank last killed text (@code{yank}). @item M-y -Replace text just yanked with an earlier batch of killed text -(@code{yank-pop}). +Choose previously yanked text to insert (@code{yank-previous}). @item M-w Save region as last killed text without actually killing it (@code{kill-ring-save}). @@ -374,49 +373,94 @@ @cindex yanking previous kills @kindex M-y address@hidden yank-previous @findex yank-pop - To recover killed text that is no longer the most recent kill, use the address@hidden command (@code{yank-pop}). It takes the text previously -yanked and replaces it with the text from an earlier kill. So, to -recover the text of the next-to-the-last kill, first use @kbd{C-y} to -yank the last kill, and then use @kbd{M-y} to replace it with the -previous kill. @kbd{M-y} is allowed only after a @kbd{C-y} or another address@hidden - - You can understand @kbd{M-y} in terms of a ``last yank'' pointer which -points at an entry in the kill ring. Each time you kill, the ``last -yank'' pointer moves to the newly made entry at the front of the ring. address@hidden yanks the entry which the ``last yank'' pointer points to. address@hidden moves the ``last yank'' pointer to a different entry, and the -text in the buffer changes to match. Enough @kbd{M-y} commands can move -the pointer to any entry in the ring, so you can get any entry into the address@hidden kill-ring-menu + To recover killed text that is no longer the most recent kill, use +the @kbd{M-y} command (@code{yank-previous}). This command can act in +two different ways. If the previous command was not a yank, then address@hidden will display a menu of kill ring entries, which you can +choose from (@code{kill-ring-menu}). If the previous command was a +yank, then @kbd{M-y} will replace the previously yanked text with the +text from an earlier kill (@code{yank-pop}). + address@hidden The Kill Ring Menu +If you are not sure how long ago you killed the piece of text you're +loooking for, it can often be easiest to use the kill ring menu. +Simply type @kbd{M-y} (which was not preceeded by a @kbd{C-y}), and a +window should pop up, which will display the text previously killed, +one entry on each line. In this special mode, keys do not insert +themselves; instead, they choose actions to perform. + address@hidden @kbd address@hidden RET +Choose the text at point to yank (@code{kill-ring-menu-insert}). address@hidden n +Move point forward by one kill ring entry +(@code{kill-ring-menu-forward}). address@hidden p +Move point backwards by one kill ring entry +(@code{kill-ring-menu-backward}). address@hidden q +Quit the kill ring menu without yanking any text +(@code{kill-ring-menu-quit}). address@hidden d +Remove the text at point from the kill ring +(@code{kill-ring-menu-delete}). address@hidden table + +The kill ring menu can display multi-line text; In the default display +style, all text is shown on one line, and newlines are shown as a address@hidden + address@hidden Cycling Through Yanked Text + +If you know precisely where the text you previously yanked is in the address@hidden, or it was very recently yanked, you may find using +the @code{yank-pop} functionality of @kbd{M-y} to be faster than the +kill ring menu. For example, to recover the text of the +next-to-the-last kill, you could first use @kbd{C-y} to yank the last +kill, and then type @kbd{M-y} to replace it with the previous kill. + + You can understand @code{yank-pop} in terms of a ``last yank'' +pointer which points at an entry in the kill ring. Each time you +kill, the ``last yank'' pointer moves to the newly made entry at the +front of the ring. @kbd{C-y} yanks the entry which the ``last yank'' +pointer points to. After that, typing @kbd{M-y} moves the ``last +yank'' pointer to a different entry, and the text in the buffer +changes to match. Enough @code{yank-pop} commands can move the +pointer to any entry in the ring, so you can get any entry into the buffer. Eventually the pointer reaches the end of the ring; the next @kbd{M-y} loops back around to the first entry again. - @kbd{M-y} moves the ``last yank'' pointer around the ring, but it does -not change the order of the entries in the ring, which always runs from -the most recent kill at the front to the oldest one still remembered. - - @kbd{M-y} can take a numeric argument, which tells it how many entries -to advance the ``last yank'' pointer by. A negative argument moves the -pointer toward the front of the ring; from the front of the ring, it -moves ``around'' to the last entry and continues forward from there. - - Once the text you are looking for is brought into the buffer, you can -stop doing @kbd{M-y} commands and it will stay there. It's just a copy -of the kill ring entry, so editing it in the buffer does not change -what's in the ring. As long as no new killing is done, the ``last -yank'' pointer remains at the same place in the kill ring, so repeating address@hidden will yank another copy of the same previous kill. - - If you know how many @kbd{M-y} commands it would take to find the -text you want, you can yank that text in one step using @kbd{C-y} with -a numeric argument. @kbd{C-y} with an argument restores the text from -the specified kill ring entry, counting back from the most recent as -1. Thus, @kbd{C-u 2 C-y} gets the next-to-the-last block of killed + @code{yank-pop} moves the ``last yank'' pointer around the ring, but +it does not change the order of the entries in the ring, which always +runs from the most recent kill at the front to the oldest one still +remembered. + + @code{yank-pop} can take a numeric argument, which tells it how many +entries to advance the ``last yank'' pointer by. A negative argument +moves the pointer toward the front of the ring; from the front of the +ring, it moves ``around'' to the last entry and continues forward from +there. + + Once the text you are looking for is brought into the buffer, you +can stop doing @code{yank-pop} commands and it will stay there. It's +just a copy of the kill ring entry, so editing it in the buffer does +not change what's in the ring. As long as no new killing is done, the +``last yank'' pointer remains at the same place in the kill ring, so +repeating @kbd{C-y} will yank another copy of the same previous kill. + + If you know how many @code{yank-pop} commands it would take to find +the text you want, you can yank that text in one step using @kbd{C-y} +with a numeric argument. @kbd{C-y} with an argument restores the text +from the specified kill ring entry, counting back from the most recent +as 1. Thus, @kbd{C-u 2 C-y} gets the next-to-the-last block of killed text---it is equivalent to @kbd{C-y M-y}. @kbd{C-y} with a numeric argument starts counting from the ``last yank'' pointer, and sets the ``last yank'' pointer to the entry that it yanks. + address@hidden The Kill Ring Data @vindex kill-ring-max The length of the kill ring is controlled by the variable Index: simple.el =================================================================== RCS file: /cvsroot/emacs/emacs/lisp/simple.el,v retrieving revision 1.539 diff -u -u -r1.539 simple.el --- simple.el 27 Apr 2002 23:16:18 -0000 1.539 +++ simple.el 29 Apr 2002 04:29:23 -0000 @@ -1713,9 +1713,69 @@ :type 'integer :group 'killing) +(defcustom browse-kill-ring-display-style 'one-line + "How to display the kill ring menu items. + +If `one-line', then replace newlines with \"\\n\" for display. + +If `separated', then display `browse-kill-ring-separator' between +entries." + :type '(choice (const :tag "One line" one-line) + (const :tag "Separated" separated)) + :group 'killing) + +(defcustom browse-kill-ring-quit-action 'bury-and-delete-window + "What action to take when `browse-kill-ring-quit' is called. + +If `bury-buffer', then simply bury the *Kill Ring* buffer, but keep +the window. + +If `bury-and-delete-window', then bury the buffer, and (if there is +more than one window) delete the window. This is the default. + +If `kill-and-delete-window', then kill the *Kill Ring* buffer, and +delete the window on close. + +Otherwise, it should be a function to call." + :type '(choice (const :tag "Bury buffer" :value bury-buffer) + (const :tag "Delete window" :value delete-window) + (const :tag "Bury buffer and delete window" :value bury-and-delete-window) + (const :tag "Kill buffer and delete window" :value kill-and-delete-window)) + :group 'killing) + +(defcustom browse-kill-ring-maximum-display-length nil + "Whether or not to limit the length of displayed items. + +If this variable is an integer, the display of `kill-ring' will be +limited to that many characters. +Setting this variable to nil means no limit." + :type '(choice (const :tag "None" nil) + integer) + :group 'browse-kill-ring) + +(defcustom browse-kill-ring-separator "-------" + "The string separating entries in the `separated' style. +See `browse-kill-ring-display-style'." + :type 'string + :group 'killing) + +(defcustom browse-kill-ring-highlight-current-entry nil + "If non-nil, highlight the currently selected `kill-ring' entry." + :type 'boolean + :group 'killing) + +(defface browse-kill-ring-separator-face '((t :inherit bold)) + "The face in which to highlight the `browse-kill-ring-separator'." + :group 'killing) + (defvar kill-ring-yank-pointer nil "The tail of the kill ring whose car is the last thing yanked.") +(defvar browse-kill-ring-original-window nil + "The window in which chosen kill ring data will be inserted. +It is probably not a good idea to set this variable directly; simply +call `browse-kill-ring' again.") + (defun kill-new (string &optional replace) "Make STRING the latest kill in the kill ring. Set `kill-ring-yank-pointer' to point to it. @@ -1770,8 +1830,6 @@ (setq kill-ring-yank-pointer ARGth-kill-element)) (car ARGth-kill-element))))) - - ;;;; Commands for manipulating the kill ring. (defcustom kill-read-only-ok nil @@ -1921,8 +1979,8 @@ The sequence of kills wraps around, so that after the oldest one comes the newest one." (interactive "*p") - (if (not (eq last-command 'yank)) - (error "Previous command was not a yank")) + (unless (eq last-command 'yank) + (error "Previous command was not a yank")) (setq this-command 'yank) (let ((inhibit-read-only t) (before (< (point) (mark t)))) @@ -1937,6 +1995,18 @@ (set-marker (mark-marker) (point) (current-buffer)))))) nil) +(defun yank-previous (arg) + "Replace previously yanked text, or display a kill ring menu. +If the last command was a `yank', `yank-previous', or `yank-pop', then +this command will replace a just-yanked section of text with a prevous +entry in the kill ring, using ARG. See `yank-pop'. +Otherwise, this command will display a menu of kill ring entries; see +`browse-kill-ring'." + (interactive "*p") + (if (not (memq last-command '(yank yank-previous))) + (browse-kill-ring) + (yank-pop arg))) + (defun yank (&optional arg) "Reinsert the last stretch of killed text. More precisely, reinsert the stretch of killed text most recently @@ -1983,6 +2053,251 @@ (if (listp arg) (setq arg (car arg))) (if (eq arg '-) (setq arg -1)) (kill-region (point) (forward-point (- arg)))) + +(put 'browse-kill-ring-mode 'mode-class 'special) +(define-derived-mode browse-kill-ring-mode fundamental-mode + "Kill Ring" + "A major mode for browsing the `kill-ring'. +You most likely do not want to call `browse-kill-ring-mode' directly; use +`browse-kill-ring' instead. + +\\{browse-kill-ring-mode-map}" + (set (make-local-variable 'browse-kill-ring-original-window) nil) + (set (make-local-variable 'font-lock-defaults) + '(nil t nil nil nil + (font-lock-fontify-region-function . browse-kill-ring-fontify-region-function) + (font-lock-unfontify-region-function . browse-kill-ring-unfontify-region-function))) + (define-key browse-kill-ring-mode-map (kbd "q") 'browse-kill-ring-quit) + (define-key browse-kill-ring-mode-map (kbd "C-g") 'browse-kill-ring-quit) + (define-key browse-kill-ring-mode-map (kbd "d") 'browse-kill-ring-delete) + (define-key browse-kill-ring-mode-map (kbd "g") 'browse-kill-ring-update) + (define-key browse-kill-ring-mode-map (kbd "n") 'browse-kill-ring-forward) + (define-key browse-kill-ring-mode-map (kbd "p") 'browse-kill-ring-backward) + (define-key browse-kill-ring-mode-map [(mouse-2)] 'browse-kill-ring-mouse-insert) + (define-key browse-kill-ring-mode-map (kbd "?") 'describe-mode) + (define-key browse-kill-ring-mode-map (kbd "h") 'describe-mode) + (define-key browse-kill-ring-mode-map (kbd "RET") 'browse-kill-ring-insert)) + +(defun browse-kill-ring-fontify-region-function (beg end &optional verbose) + (when verbose (message "Fontifying...")) + (let ((inhibit-read-only t)) + (font-lock-fontify-on-text-properties + beg end 'browse-kill-ring-special 'bold) + (when verbose (message "Fontifying...done")))) + +(defun browse-kill-ring-unfontify-region-function (beg end) + (let ((inhibit-read-only t)) + (remove-text-properties beg end '(face nil)))) + +(defun browse-kill-ring-elide (str) + (if (and browse-kill-ring-maximum-display-length + (> (length str) + browse-kill-ring-maximum-display-length)) + (concat (substring str 0 (- browse-kill-ring-maximum-display-length 3)) + (propertize "..." 'browse-kill-ring-special t))) + str) + +(defmacro browse-kill-ring-add-overlays-for (item &rest body) + (let ((beg (gensym "browse-kill-ring-add-overlays-")) + (end (gensym "browse-kill-ring-add-overlays-"))) + `(let ((,beg (point)) + (,end + (progn + ,@body + (point)))) + (let ((o (make-overlay ,beg ,end))) + (overlay-put o 'browse-kill-ring-target ,item) + (overlay-put o 'mouse-face 'highlight))))) +;; (put 'browse-kill-ring-add-overlays-for 'lisp-indent-function 1) + +(defun browse-kill-ring-insert-as-one-line (items) + (dolist (item items) + (browse-kill-ring-add-overlays-for item + (let* ((item (browse-kill-ring-elide item)) + (len (length item)) + (start 0) + (newl (propertize "\\n" 'browse-kill-ring-special t))) + (while (and (< start len) + (string-match "\n" item start)) + (insert (substring item start (match-beginning 0)) + newl) + (setq start (match-end 0))) + (insert (substring item start len)))) + (insert "\n"))) + +(defun browse-kill-ring-insert-as-separated (items) + (while (cdr items) + (browse-kill-ring-insert-as-separated-1 (car items) t) + (setq items (cdr items))) + (when items + (browse-kill-ring-insert-as-separated-1 (car items) nil))) + +(defun browse-kill-ring-insert-as-separated-1 (origitem separatep) + (let* ((item (browse-kill-ring-elide origitem)) + (len (length item))) + (browse-kill-ring-add-overlays-for origitem + (insert item)) + (insert "\n") + (when separatep + (insert (propertize browse-kill-ring-separator + 'browse-kill-ring-special t)) + (insert "\n")))) + +;; (unintern 'browse-kill-ring-add-overlays-for) + +(defun browse-kill-ring-quit () + "Take the action specified by `browse-kill-ring-quit-action'." + (interactive) + (case browse-kill-ring-quit-action + (kill-and-delete-window + (kill-buffer (current-buffer)) + (unless (= (count-windows) 1) + (delete-window))) + (bury-and-delete-window + (bury-buffer) + (unless (= (count-windows) 1) + (delete-window))) + (t + (funcall browse-kill-ring-quit-action)))) + +(defun browse-kill-ring-forward (&optional arg) + "Move forward by ARG `kill-ring' entries." + (interactive "p") + (beginning-of-line) + (while (not (zerop arg)) + (if (< arg 0) + (progn + (incf arg) + (if (overlays-at (point)) + (progn + (goto-char (overlay-start (car (overlays-at (point))))) + (goto-char (previous-overlay-change (point))) + (goto-char (previous-overlay-change (point)))) + (progn + (goto-char (1- (previous-overlay-change (point)))) + (unless (bobp) + (goto-char (overlay-start (car (overlays-at (point))))))))) + (progn + (decf arg) + (if (overlays-at (point)) + (progn + (goto-char (overlay-end (car (overlays-at (point))))) + (goto-char (next-overlay-change (point)))) + (goto-char (next-overlay-change (point))) + (unless (eobp) + (goto-char (overlay-start (car (overlays-at (point)))))))))) + ;; This could probably be implemented in a more intelligent manner. + ;; Perhaps keep track over the overlay we started from? That would + ;; break when the user moved manually, though. + (when (and browse-kill-ring-highlight-current-entry + (overlays-at (point))) + (let ((overs (overlay-lists)) + (current-overlay (car (overlays-at (point))))) + (mapcar #'(lambda (o) + (overlay-put o 'face nil)) + (nconc (car overs) (cdr overs))) + (overlay-put current-overlay 'face 'highlight)))) + +(defun browse-kill-ring-backward (&optional arg) + "Move backward by ARG `kill-ring' entries." + (interactive "p") + (browse-kill-ring-forward (- arg))) + +(defun browse-kill-ring-do-insert (buf pt) + (let ((str (with-current-buffer buf + (let ((overs (overlays-at pt))) + (or (and overs + (overlay-get (car overs) 'browse-kill-ring-target)) + (error "No kill ring item here")))))) + (let ((orig (current-buffer))) + (unwind-protect + (progn + (unless (window-live-p browse-kill-ring-original-window) + (browse-kill-ring-quit) + (error (substitute-command-keys "Window %s has been deleted; Try calling '\\[yank-previous]' again") + browse-kill-ring-original-window)) + (set-buffer (window-buffer browse-kill-ring-original-window)) + (save-excursion + (insert str))) + (set-buffer orig))))) + +(defun browse-kill-ring-mouse-insert (e) + "Insert the chosen text in the last selected buffer." + (interactive "e") + (let* ((end (event-end e)) + (win (posn-window end)) + (buf (window-buffer win))) + (select-window win) + (browse-kill-ring-do-insert buf (posn-point end)) + (browse-kill-ring-quit))) + +(defun browse-kill-ring-insert (&optional noquit) + "Insert the kill ring item at point, and close the kill ring menu. +If optional argument NOQUIT is non-nil, don't close the menu." + (interactive "P") + (browse-kill-ring-do-insert (current-buffer) (point)) + (unless noquit + (browse-kill-ring-quit))) + +(defun browse-kill-ring-update () + "Update the buffer to reflect outside changes to `kill-ring'." + (interactive) + (assert (eq major-mode 'browse-kill-ring-mode)) + (browse-kill-ring-setup (current-buffer) + browse-kill-ring-original-window)) + +(defun browse-kill-ring-setup (buf window &optional regexp) + (with-current-buffer buf + (unwind-protect + (progn + (browse-kill-ring-mode) + (setq buffer-read-only nil) + (when (eq browse-kill-ring-display-style + 'one-line) + (setq truncate-lines t)) + (erase-buffer) + (setq browse-kill-ring-original-window window) + (let ((browse-kill-ring-maximum-display-length + (if (and browse-kill-ring-maximum-display-length + (<= browse-kill-ring-maximum-display-length 3)) + 4 + browse-kill-ring-maximum-display-length)) + (items (mapcar #'copy-sequence kill-ring))) +;; (when (not browse-kill-ring-display-duplicates) +;; (setq items (delete-duplicates items :test #'equal))) + (when (stringp regexp) + (setq items (delq nil + (mapcar + #'(lambda (item) + (when (string-match regexp item) + item)) + items)))) + (funcall (intern + (concat "browse-kill-ring-insert-as-" + (symbol-name browse-kill-ring-display-style))) + items) + (if (not regexp) + (message "%s entries in the kill ring" (length kill-ring)) + (message "%s (of %s) entries in the kill ring shown" + (length items) (length kill-ring))) + (set-buffer-modified-p nil) + (goto-char (point-min)) + (browse-kill-ring-forward 0) + (when regexp + (setq mode-name (concat "Kill Ring [" regexp "]"))) + (run-hooks 'browse-kill-ring-hook))) + (progn + (setq buffer-read-only t))))) + +;;;###autoload +(defun browse-kill-ring () + "Display items in the `kill-ring' in another buffer." + (interactive) + (let ((orig-buf (current-buffer)) + (buf (get-buffer-create "*Kill Ring*"))) + (browse-kill-ring-setup buf (selected-window)) + (pop-to-buffer buf) + nil)) (defcustom backward-delete-char-untabify-method 'untabify "*The method for untabifying when deleting backward. --- font-lock.el.~1.195.~ Fri Apr 5 04:37:40 2002 +++ font-lock.el Sun Apr 28 22:31:57 2002 @@ -1358,6 +1358,32 @@ (put-text-property start next prop value object) (setq start (text-property-any next end prop nil object))))) +(defun font-lock-fontify-on-text-properties (beg end &rest plist) + "Match areas of text which have non-nil text properties with faces. +Only areas of text between BEG and END are considered. The remaining +arguments should form a property list which looks like + (PROPERTY FACE PROPERTY FACE ...)." + (save-excursion + (let ((change-end nil)) + (while plist + (goto-char beg) + (while (and plist + (setq change-end (font-lock-fontify-on-text-property + (car plist) (cadr plist) (point) end))) + (goto-char change-end)) + (setq plist (cddr plist)))))) + +(defun font-lock-fontify-on-text-property (prop face beg end) + "Add face FACE to areas of text which have non-nil text property PROP. +Only areas of text between BEG and END are considered." + (let ((prop-beg (or (and (get-text-property (point) prop) (point)) + (next-single-property-change (point) prop nil end)))) + (when (and prop-beg (not (= prop-beg end))) + (let ((prop-end (next-single-property-change beg prop nil end))) + (when (and prop-end (not (= prop-end end))) + (put-text-property prop-beg prop-end 'face face) + prop-end))))) + ;; For completeness: this is to `remove-text-properties' as `put-text-property' ;; is to `add-text-properties', etc. ;(defun remove-text-property (start end property &optional object) --- bindings.el.~1.100.~ Wed Apr 17 17:38:22 2002 +++ bindings.el Sun Apr 28 02:09:00 2002 @@ -643,7 +643,7 @@ (define-key esc-map "w" 'kill-ring-save) (define-key esc-map "\C-w" 'append-next-kill) (define-key global-map "\C-y" 'yank) -(define-key esc-map "y" 'yank-pop) +(define-key esc-map "y" 'yank-previous) ;; (define-key ctl-x-map "a" 'append-to-buffer)