changelog shortlog tags changeset files revisions annotate raw

browse-kill-ring.el

changeset 66: 5b737eefe5ea
author: kim.vanwyk
date: Wed Nov 10 15:19:03 2010 +0200 (18 months ago)
permissions: -rw-r--r--
description: Adding CSharp Mode and Google Weather
1;;; browse-kill-ring.el --- interactively insert items from kill-ring -*- coding: utf-8 -*-
2
3;; Copyright (C) 2001, 2002 Colin Walters <walters@verbum.org>
4
5;; Author: Colin Walters <walters@verbum.org>
6;; Maintainer: Nick Hurley <hurley@cis.ohio-state.edu>
7;; Created: 7 Apr 2001
8;; Version: 1.3a (CVS)
9;; X-RCS: $Id: browse-kill-ring.el,v 1.2 2008/10/29 00:23:00 hurley Exp $
10;; URL: http://freedom.cis.ohio-state.edu/~hurley/
11;; URL-ja: http://www.fan.gr.jp/~ring/doc/browse-kill-ring.html
12;; Keywords: convenience
13
14;; This file is not currently part of GNU Emacs.
15
16;; This program is free software; you can redistribute it and/or
17;; modify it under the terms of the GNU General Public License as
18;; published by the Free Software Foundation; either version 2, or (at
19;; your option) any later version.
20
21;; This program is distributed in the hope that it will be useful, but
22;; WITHOUT ANY WARRANTY; without even the implied warranty of
23;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24;; General Public License for more details.
25
26;; You should have received a copy of the GNU General Public License
27;; along with this program ; see the file COPYING. If not, write to
28;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
29;; Boston, MA 02111-1307, USA.
30
31;;; Commentary:
32
33;; Ever feel that 'C-y M-y M-y M-y ...' is not a great way of trying
34;; to find that piece of text you know you killed a while back? Then
35;; browse-kill-ring.el is for you.
36
37;; This package is simple to install; add (require 'browse-kill-ring)
38;; to your ~/.emacs file, after placing this file somewhere in your
39;; `load-path'. If you want to use 'M-y' to invoke
40;; `browse-kill-ring', also add (browse-kill-ring-default-keybindings)
41;; to your ~/.emacs file. Alternatively, you can bind it to another
42;; key such as "C-c k", with:
43;; (global-set-key (kbd "C-c k") 'browse-kill-ring)
44
45;; Note that the command keeps track of the last window displayed to
46;; handle insertion of chosen text; this might have unexpected
47;; consequences if you do 'M-x browse-kill-ring', then switch your
48;; window configuration, and try to use the same *Kill Ring* buffer
49;; again.
50
51;;; Change Log:
52
53;; Changes from 1.3 to 1.3a:
54
55;; * Sneak update by Benjamin Andresen <bandresen@gmail.com>
56;; * Added the read-only bugfix (http://bugs.debian.org/225082) from
57;; the emacs-goodies-el package
58
59;; Changes from 1.2 to 1.3:
60
61;; * New maintainer, Nick Hurley <hurley@cis.ohio-state.edu>
62;; * New functions `browse-kill-ring-prepend-insert', and
63;; `browse-kill-ring-append-insert', bound to 'b' and 'a' by
64;; default. There are also the unbound functions
65;; `browse-kill-ring-prepend-insert-and-quit',
66;; `browse-kill-ring-prepend-insert-and-move',
67;; `browse-kill-ring-prepend-insert-move-and-quit',
68;; `browse-kill-ring-append-insert-and-quit',
69;; `browse-kill-ring-append-insert-and-move',
70;; `browse-kill-ring-append-insert-move-and-quit'.
71
72;; Changes from 1.1 to 1.2:
73
74;; * New variable `browse-kill-ring-resize-window', which controls
75;; whether or not the browse-kill-ring window will try to resize
76;; itself to fit the buffer. Implementation from Juanma Barranquero
77;; <lektu@terra.es>.
78;; * New variable `browse-kill-ring-highlight-inserted-item'.
79;; Implementation from Yasutaka SHINDOH <ring-pub@fan.gr.jp>.
80;; * `browse-kill-ring-mouse-insert' (normally bound to mouse-2) now
81;; calls `browse-kill-ring-quit'.
82;; * Some non-user-visible code cleanup.
83;; * New variable `browse-kill-ring-recenter', implementation from
84;; René Kyllingstad <kyllingstad@users.sourceforge.net>.
85;; * Patch from Michal Maršuka <mmc@maruska.dyndns.org> which handles
86;; read-only text better.
87;; * New ability to move unkilled entries back to the beginning of the
88;; ring; patch from Yasutaka SHINDOH <ring-pub@fan.gr.jp>.
89;; * Do nothing if the user invokes `browse-kill-ring' when we're
90;; already in a *Kill Ring* buffer (initial patch from Juanma
91;; Barranquero <lektu@terra.es>).
92
93;; Changes from 1.0 to 1.1:
94
95;; * Important keybinding change! The default bindings of RET and 'i'
96;; have switched; this means typing RET now by default inserts the
97;; text and calls `browse-kill-ring-quit'; 'i' just inserts.
98;; * The variable `browse-kill-ring-use-fontification' is gone;
99;; browse-kill-ring.el has been rewritten to use font-lock. XEmacs
100;; users who want fontification will have to do:
101;; (add-hook 'browse-kill-ring-hook 'font-lock-mode)
102;; * Integrated code from Michael Slass <mikesl@wrq.com> into
103;; `browse-kill-ring-default-keybindings'.
104;; * New Japanese homepage for browse-kill-ring.el, thanks to
105;; Yasutaka SHINDOH <ring-pub@fan.gr.jp>.
106;; * Correctly restore window configuration after editing an entry.
107;; * New command `browse-kill-ring-insert-and-delete'.
108;; * Bug reports and patches from Michael Slass <mikesl@wrq.com> and
109;; Juanma Barranquero <lektu@terra.es>.
110
111;; Changes from 0.9b to 1.0:
112
113;; * Add autoload cookie to `browse-kill-ring'; suggestion from
114;; D. Goel <deego@glue.umd.edu> and Dave Pearson <davep@davep.org>.
115;; * Add keybinding tip from Michael Slass <mikesl@wrq.com>.
116
117;; Changes from 0.9a to 0.9b:
118
119;; * Remove extra parenthesis. Duh.
120
121;; Changes from 0.9 to 0.9a:
122
123;; * Fix bug making `browse-kill-ring-quit-action' uncustomizable.
124;; Patch from Henrik Enberg <henrik@enberg.org>.
125;; * Add `url-link' and `group' attributes to main Customization
126;; group.
127
128;; Changes from 0.8 to 0.9:
129
130;; * Add new function `browse-kill-ring-insert-and-quit', bound to 'i'
131;; by default (idea from Yasutaka Shindoh).
132;; * Make default `browse-kill-ring-quit-action' be
133;; `bury-and-delete-window', which handles the case of a single window
134;; more nicely.
135;; * Note change of home page and author address.
136
137;; Changes from 0.7 to 0.8:
138
139;; * Fix silly bug in `browse-kill-ring-edit' which made it impossible
140;; to edit entries.
141;; * New variable `browse-kill-ring-quit-action'.
142;; * `browse-kill-ring-restore' renamed to `browse-kill-ring-quit'.
143;; * Describe the keymaps in mode documentation. Patch from
144;; Marko Slyz <mslyz@eecs.umich.edu>.
145;; * Fix advice documentation for `browse-kill-ring-no-duplicates'.
146
147;; Changes from 0.6 to 0.7:
148
149;; * New functions `browse-kill-ring-search-forward' and
150;; `browse-kill-ring-search-backward', bound to "s" and "r" by
151;; default, respectively.
152;; * New function `browse-kill-ring-edit' bound to "e" by default, and
153;; a associated new major mode.
154;; * New function `browse-kill-ring-occur', bound to "l" by default.
155
156;; Changes from 0.5 to 0.6:
157
158;; * Fix bug in `browse-kill-ring-forward' which sometimes would cause
159;; a message "Wrong type argument: overlayp, nil" to appear.
160;; * New function `browse-kill-ring-update'.
161;; * New variable `browse-kill-ring-highlight-current-entry'.
162;; * New variable `browse-kill-ring-display-duplicates'.
163;; * New optional advice `browse-kill-ring-no-kill-new-duplicates',
164;; and associated variable `browse-kill-ring-no-duplicates'. Code
165;; from Klaus Berndl <Klaus.Berndl@sdm.de>.
166;; * Bind "?" to `describe-mode'. Patch from Dave Pearson
167;; <dave@davep.org>.
168;; * Fix typo in `browse-kill-ring-display-style' defcustom form.
169;; Thanks "Kahlil (Kal) HODGSON" <kahlil@discus.anu.edu.au>.
170
171;; Changes from 0.4 to 0.5:
172
173;; * New function `browse-kill-ring-delete', bound to "d" by default.
174;; * New function `browse-kill-ring-undo', bound to "U" by default.
175;; * New variable `browse-kill-ring-maximum-display-length'.
176;; * New variable `browse-kill-ring-use-fontification'.
177;; * New variable `browse-kill-ring-hook', called after the
178;; "*Kill Ring*" buffer is created.
179
180;; Changes from 0.3 to 0.4:
181
182;; * New functions `browse-kill-ring-forward' and
183;; `browse-kill-ring-previous', bound to "n" and "p" by default,
184;; respectively.
185;; * Change the default `browse-kill-ring-display-style' to
186;; `separated'.
187;; * Removed `browse-kill-ring-original-window-config'; Now
188;; `browse-kill-ring-restore' just buries the "*Kill Ring*" buffer
189;; and deletes its window, which is simpler and more intuitive.
190;; * New variable `browse-kill-ring-separator-face'.
191
192;;; Bugs:
193
194;; * Sometimes, in Emacs 21, the cursor will jump to the end of an
195;; entry when moving backwards using `browse-kill-ring-previous'.
196;; This doesn't seem to occur in Emacs 20 or XEmacs.
197
198;;; Code:
199
200(eval-when-compile
201 (require 'cl)
202 (require 'derived))
203
204(when (featurep 'xemacs)
205 (require 'overlay))
206
207(defun browse-kill-ring-depropertize-string (str)
208 "Return a copy of STR with text properties removed."
209 (let ((str (copy-sequence str)))
210 (set-text-properties 0 (length str) nil str)
211 str))
212
213(cond ((fboundp 'propertize)
214 (defalias 'browse-kill-ring-propertize 'propertize))
215 ;; Maybe save some memory :)
216 ((fboundp 'ibuffer-propertize)
217 (defalias 'browse-kill-ring-propertize 'ibuffer-propertize))
218 (t
219 (defun browse-kill-ring-propertize (string &rest properties)
220 "Return a copy of STRING with text properties added.
221
222 [Note: this docstring has been copied from the Emacs 21 version]
223
224First argument is the string to copy.
225Remaining arguments form a sequence of PROPERTY VALUE pairs for text
226properties to add to the result."
227 (let ((str (copy-sequence string)))
228 (add-text-properties 0 (length str)
229 properties
230 str)
231 str))))
232
233(defgroup browse-kill-ring nil
234 "A package for browsing and inserting the items in `kill-ring'."
235 :link '(url-link "http://freedom.cis.ohio-state.edu/~hurley/")
236 :group 'convenience)
237
238(defvar browse-kill-ring-display-styles
239 '((separated . browse-kill-ring-insert-as-separated)
240 (one-line . browse-kill-ring-insert-as-one-line)))
241
242(defcustom browse-kill-ring-display-style 'separated
243 "How to display the kill ring items.
244
245If `one-line', then replace newlines with \"\\n\" for display.
246
247If `separated', then display `browse-kill-ring-separator' between
248entries."
249 :type '(choice (const :tag "One line" one-line)
250 (const :tag "Separated" separated))
251 :group 'browse-kill-ring)
252
253(defcustom browse-kill-ring-quit-action 'bury-and-delete-window
254 "What action to take when `browse-kill-ring-quit' is called.
255
256If `bury-buffer', then simply bury the *Kill Ring* buffer, but keep
257the window.
258
259If `bury-and-delete-window', then bury the buffer, and (if there is
260more than one window) delete the window. This is the default.
261
262If `save-and-restore', then save the window configuration when
263`browse-kill-ring' is called, and restore it at quit.
264
265If `kill-and-delete-window', then kill the *Kill Ring* buffer, and
266delete the window on close.
267
268Otherwise, it should be a function to call."
269 :type '(choice (const :tag "Bury buffer" :value bury-buffer)
270 (const :tag "Delete window" :value delete-window)
271 (const :tag "Save and restore" :value save-and-restore)
272 (const :tag "Bury buffer and delete window" :value bury-and-delete-window)
273 (const :tag "Kill buffer and delete window" :value kill-and-delete-window)
274 function)
275 :group 'browse-kill-ring)
276
277(defcustom browse-kill-ring-resize-window nil
278 "Whether to resize the `browse-kill-ring' window to fit its contents.
279Value is either t, meaning yes, or a cons pair of integers,
280 (MAXIMUM . MINIMUM) for the size of the window. MAXIMUM defaults to
281the window size chosen by `pop-to-buffer'; MINIMUM defaults to
282`window-min-height'."
283 :type '(choice (const :tag "No" nil)
284 (const :tag "Yes" t)
285 (cons (integer :tag "Maximum") (integer :tag "Minimum")))
286 :group 'browse-kill-ring)
287
288(defcustom browse-kill-ring-separator "-------"
289 "The string separating entries in the `separated' style.
290See `browse-kill-ring-display-style'."
291 :type 'string
292 :group 'browse-kill-ring)
293
294(defcustom browse-kill-ring-recenter nil
295 "If non-nil, then always keep the current entry at the top of the window."
296 :type 'boolean
297 :group 'browse-kill-ring)
298
299(defcustom browse-kill-ring-highlight-current-entry nil
300 "If non-nil, highlight the currently selected `kill-ring' entry."
301 :type 'boolean
302 :group 'browse-kill-ring)
303
304(defcustom browse-kill-ring-highlight-inserted-item browse-kill-ring-highlight-current-entry
305 "If non-nil, temporarily highlight the inserted `kill-ring' entry."
306 :type 'boolean
307 :group 'browse-kill-ring)
308
309(defcustom browse-kill-ring-separator-face 'bold
310 "The face in which to highlight the `browse-kill-ring-separator'."
311 :type 'face
312 :group 'browse-kill-ring)
313
314(defcustom browse-kill-ring-maximum-display-length nil
315 "Whether or not to limit the length of displayed items.
316
317If this variable is an integer, the display of `kill-ring' will be
318limited to that many characters.
319Setting this variable to nil means no limit."
320 :type '(choice (const :tag "None" nil)
321 integer)
322 :group 'browse-kill-ring)
323
324(defcustom browse-kill-ring-display-duplicates t
325 "If non-nil, then display duplicate items in `kill-ring'."
326 :type 'boolean
327 :group 'browse-kill-ring)
328
329(defadvice kill-new (around browse-kill-ring-no-kill-new-duplicates)
330 "An advice for not adding duplicate elements to `kill-ring'.
331Even after being \"activated\", this advice will only modify the
332behavior of `kill-new' when `browse-kill-ring-no-duplicates'
333is non-nil."
334 (if browse-kill-ring-no-duplicates
335 (setq kill-ring (delete (ad-get-arg 0) kill-ring)))
336 ad-do-it)
337
338(defcustom browse-kill-ring-no-duplicates nil
339 "If non-nil, then the `b-k-r-no-kill-new-duplicates' advice will operate.
340This means that duplicate entries won't be added to the `kill-ring'
341when you call `kill-new'.
342
343If you set this variable via customize, the advice will be activated
344or deactivated automatically. Otherwise, to enable the advice, add
345
346 (ad-enable-advice 'kill-new 'around 'browse-kill-ring-no-kill-new-duplicates)
347 (ad-activate 'kill-new)
348
349to your init file."
350 :type 'boolean
351 :set (lambda (symbol value)
352 (set symbol value)
353 (if value
354 (ad-enable-advice 'kill-new 'around
355 'browse-kill-ring-no-kill-new-duplicates)
356 (ad-disable-advice 'kill-new 'around
357 'browse-kill-ring-no-kill-new-duplicates))
358 (ad-activate 'kill-new))
359 :group 'browse-kill-ring)
360
361(defcustom browse-kill-ring-depropertize nil
362 "If non-nil, remove text properties from `kill-ring' items.
363This only changes the items for display and insertion from
364`browse-kill-ring'; if you call `yank' directly, the items will be
365inserted with properties."
366 :type 'boolean
367 :group 'browse-kill-ring)
368
369(defcustom browse-kill-ring-hook nil
370 "A list of functions to call after `browse-kill-ring'."
371 :type 'hook
372 :group 'browse-kill-ring)
373
374(defvar browse-kill-ring-original-window-config nil
375 "The window configuration to restore for `browse-kill-ring-quit'.")
376(make-variable-buffer-local 'browse-kill-ring-original-window-config)
377
378(defvar browse-kill-ring-original-window nil
379 "The window in which chosen kill ring data will be inserted.
380It is probably not a good idea to set this variable directly; simply
381call `browse-kill-ring' again.")
382
383(defun browse-kill-ring-mouse-insert (e)
384 "Insert the chosen text, and close the *Kill Ring* buffer afterwards."
385 (interactive "e")
386 (let* ((data (save-excursion
387 (mouse-set-point e)
388 (cons (current-buffer) (point))))
389 (buf (car data))
390 (pt (cdr data)))
391 (browse-kill-ring-do-insert buf pt))
392 (browse-kill-ring-quit))
393
394(if (fboundp 'fit-window-to-buffer)
395 (defalias 'browse-kill-ring-fit-window 'fit-window-to-buffer)
396 (defun browse-kill-ring-fit-window (window max-height min-height)
397 (setq min-height (or min-height window-min-height))
398 (setq max-height (or max-height (- (frame-height) (window-height) 1)))
399 (let* ((window-min-height min-height)
400 (windows (count-windows))
401 (config (current-window-configuration)))
402 (enlarge-window (- max-height (window-height)))
403 (when (> windows (count-windows))
404 (set-window-configuration config))
405 (if (/= (point-min) (point-max))
406 (shrink-window-if-larger-than-buffer window)
407 (shrink-window (- (window-height) window-min-height))))))
408
409(defun browse-kill-ring-resize-window ()
410 (when browse-kill-ring-resize-window
411 (apply #'browse-kill-ring-fit-window (selected-window)
412 (if (consp browse-kill-ring-resize-window)
413 (list (car browse-kill-ring-resize-window)
414 (or (cdr browse-kill-ring-resize-window)
415 window-min-height))
416 (list nil window-min-height)))))
417
418(defun browse-kill-ring-undo-other-window ()
419 "Undo the most recent change in the other window's buffer.
420You most likely want to use this command for undoing an insertion of
421yanked text from the *Kill Ring* buffer."
422 (interactive)
423 (with-current-buffer (window-buffer browse-kill-ring-original-window)
424 (undo)))
425
426(defun browse-kill-ring-insert (&optional quit)
427 "Insert the kill ring item at point into the last selected buffer.
428If optional argument QUIT is non-nil, close the *Kill Ring* buffer as
429well."
430 (interactive "P")
431 (browse-kill-ring-do-insert (current-buffer)
432 (point))
433 (when quit
434 (browse-kill-ring-quit)))
435
436(defun browse-kill-ring-insert-and-delete (&optional quit)
437 "Insert the kill ring item at point, and remove it from the kill ring.
438If optional argument QUIT is non-nil, close the *Kill Ring* buffer as
439well."
440 (interactive "P")
441 (browse-kill-ring-do-insert (current-buffer)
442 (point))
443 (browse-kill-ring-delete)
444 (when quit
445 (browse-kill-ring-quit)))
446
447(defun browse-kill-ring-insert-and-quit ()
448 "Like `browse-kill-ring-insert', but close the *Kill Ring* buffer afterwards."
449 (interactive)
450 (browse-kill-ring-insert t))
451
452(defun browse-kill-ring-insert-and-move (&optional quit)
453 "Like `browse-kill-ring-insert', but move the entry to the front."
454 (interactive "P")
455 (let ((buf (current-buffer))
456 (pt (point)))
457 (browse-kill-ring-do-insert buf pt)
458 (let ((str (browse-kill-ring-current-string buf pt)))
459 (browse-kill-ring-delete)
460 (kill-new str)))
461 (if quit
462 (browse-kill-ring-quit)
463 (browse-kill-ring-update)))
464
465(defun browse-kill-ring-insert-move-and-quit ()
466 "Like `browse-kill-ring-insert-and-move', but close the *Kill Ring* buffer."
467 (interactive)
468 (browse-kill-ring-insert-and-move t))
469
470(defun browse-kill-ring-prepend-insert (&optional quit)
471 "Like `browse-kill-ring-insert', but it places the entry at the beginning
472of the buffer as opposed to point."
473 (interactive "P")
474 (browse-kill-ring-do-prepend-insert (current-buffer)
475 (point))
476 (when quit
477 (browse-kill-ring-quit)))
478
479(defun browse-kill-ring-prepend-insert-and-quit ()
480 "Like `browse-kill-ring-prepend-insert', but close the *Kill Ring* buffer."
481 (interactive)
482 (browse-kill-ring-prepend-insert t))
483
484(defun browse-kill-ring-prepend-insert-and-move (&optional quit)
485 "Like `browse-kill-ring-prepend-insert', but move the entry to the front
486of the *Kill Ring*."
487 (interactive "P")
488 (let ((buf (current-buffer))
489 (pt (point)))
490 (browse-kill-ring-do-prepend-insert buf pt)
491 (let ((str (browse-kill-ring-current-string buf pt)))
492 (browse-kill-ring-delete)
493 (kill-new str)))
494 (if quit
495 (browse-kill-ring-quit)
496 (browse-kill-ring-update)))
497
498(defun browse-kill-ring-prepend-insert-move-and-quit ()
499 "Like `browse-kill-ring-prepend-insert-and-move', but close the
500*Kill Ring* buffer."
501 (interactive)
502 (browse-kill-ring-prepend-insert-and-move t))
503
504(defun browse-kill-ring-do-prepend-insert (buf pt)
505 (let ((str (browse-kill-ring-current-string buf pt)))
506 (let ((orig (current-buffer)))
507 (unwind-protect
508 (progn
509 (unless (window-live-p browse-kill-ring-original-window)
510 (error "Window %s has been deleted; Try calling `browse-kill-ring' again"
511 browse-kill-ring-original-window))
512 (set-buffer (window-buffer browse-kill-ring-original-window))
513 (save-excursion
514 (let ((pt (point)))
515 (goto-char (point-min))
516 (insert (if browse-kill-ring-depropertize
517 (browse-kill-ring-depropertize-string str)
518 str))
519 (when browse-kill-ring-highlight-inserted-item
520 (let ((o (make-overlay (point-min) (point))))
521 (overlay-put o 'face 'highlight)
522 (sit-for 0.5)
523 (delete-overlay o)))
524 (goto-char pt))))
525 (set-buffer orig)))))
526
527(defun browse-kill-ring-append-insert (&optional quit)
528 "Like `browse-kill-ring-insert', but places the entry at the end of the
529buffer as opposed to point."
530 (interactive "P")
531 (browse-kill-ring-do-append-insert (current-buffer)
532 (point))
533 (when quit
534 (browse-kill-ring-quit)))
535
536(defun browse-kill-ring-append-insert-and-quit ()
537 "Like `browse-kill-ring-append-insert', but close the *Kill Ring* buffer."
538 (interactive)
539 (browse-kill-ring-append-insert t))
540
541(defun browse-kill-ring-append-insert-and-move (&optional quit)
542 "Like `browse-kill-ring-append-insert', but move the entry to the front
543of the *Kill Ring*."
544 (interactive "P")
545 (let ((buf (current-buffer))
546 (pt (point)))
547 (browse-kill-ring-do-append-insert buf pt)
548 (let ((str (browse-kill-ring-current-string buf pt)))
549 (browse-kill-ring-delete)
550 (kill-new str)))
551 (if quit
552 (browse-kill-ring-quit)
553 (browse-kill-ring-update)))
554
555(defun browse-kill-ring-append-insert-move-and-quit ()
556 "Like `browse-kill-ring-append-insert-and-move', but close the
557*Kill Ring* buffer."
558 (interactive)
559 (browse-kill-ring-append-insert-and-move t))
560
561(defun browse-kill-ring-do-append-insert (buf pt)
562 (let ((str (browse-kill-ring-current-string buf pt)))
563 (let ((orig (current-buffer)))
564 (unwind-protect
565 (progn
566 (unless (window-live-p browse-kill-ring-original-window)
567 (error "Window %s has been deleted; Try calling `browse-kill-ring' again"
568 browse-kill-ring-original-window))
569 (set-buffer (window-buffer browse-kill-ring-original-window))
570 (save-excursion
571 (let ((pt (point))
572 (begin-pt (point-max)))
573 (goto-char begin-pt)
574 (insert (if browse-kill-ring-depropertize
575 (browse-kill-ring-depropertize-string str)
576 str))
577 (when browse-kill-ring-highlight-inserted-item
578 (let ((o (make-overlay begin-pt (point-max))))
579 (overlay-put o 'face 'highlight)
580 (sit-for 0.5)
581 (delete-overlay o)))
582 (goto-char pt))))
583 (set-buffer orig)))))
584
585(defun browse-kill-ring-delete ()
586 "Remove the item at point from the `kill-ring'."
587 (interactive)
588 (let ((over (car (overlays-at (point)))))
589 (unless (overlayp over)
590 (error "No kill ring item here"))
591 (unwind-protect
592 (progn
593 (setq buffer-read-only nil)
594 (let ((target (overlay-get over 'browse-kill-ring-target)))
595 (delete-region (overlay-start over)
596 (1+ (overlay-end over)))
597 (setq kill-ring (delete target kill-ring)))
598 (when (get-text-property (point) 'browse-kill-ring-extra)
599 (let ((prev (previous-single-property-change (point)
600 'browse-kill-ring-extra))
601 (next (next-single-property-change (point)
602 'browse-kill-ring-extra)))
603 ;; This is some voodoo.
604 (when prev
605 (incf prev))
606 (when next
607 (incf next))
608 (delete-region (or prev (point-min))
609 (or next (point-max))))))
610 (setq buffer-read-only t)))
611 (browse-kill-ring-resize-window)
612 (browse-kill-ring-forward 0))
613
614(defun browse-kill-ring-current-string (buf pt)
615 (with-current-buffer buf
616 (let ((overs (overlays-at pt)))
617 (or (and overs
618 (overlay-get (car overs) 'browse-kill-ring-target))
619 (error "No kill ring item here")))))
620
621(defun browse-kill-ring-do-insert (buf pt)
622 (let ((str (browse-kill-ring-current-string buf pt)))
623 (let ((orig (current-buffer)))
624 (unwind-protect
625 (progn
626 (unless (window-live-p browse-kill-ring-original-window)
627 (error "Window %s has been deleted; Try calling `browse-kill-ring' again"
628 browse-kill-ring-original-window))
629 (set-buffer (window-buffer browse-kill-ring-original-window))
630 (save-excursion
631 (let ((pt (point)))
632 (insert (if browse-kill-ring-depropertize
633 (browse-kill-ring-depropertize-string str)
634 str))
635 (when browse-kill-ring-highlight-inserted-item
636 (let ((o (make-overlay pt (point))))
637 (overlay-put o 'face 'highlight)
638 (sit-for 0.5)
639 (delete-overlay o))))))
640 (set-buffer orig)))))
641
642(defun browse-kill-ring-forward (&optional arg)
643 "Move forward by ARG `kill-ring' entries."
644 (interactive "p")
645 (beginning-of-line)
646 (while (not (zerop arg))
647 (if (< arg 0)
648 (progn
649 (incf arg)
650 (if (overlays-at (point))
651 (progn
652 (goto-char (overlay-start (car (overlays-at (point)))))
653 (goto-char (previous-overlay-change (point)))
654 (goto-char (previous-overlay-change (point))))
655 (progn
656 (goto-char (1- (previous-overlay-change (point))))
657 (unless (bobp)
658 (goto-char (overlay-start (car (overlays-at (point)))))))))
659 (progn
660 (decf arg)
661 (if (overlays-at (point))
662 (progn
663 (goto-char (overlay-end (car (overlays-at (point)))))
664 (goto-char (next-overlay-change (point))))
665 (goto-char (next-overlay-change (point)))
666 (unless (eobp)
667 (goto-char (overlay-start (car (overlays-at (point))))))))))
668 ;; This could probably be implemented in a more intelligent manner.
669 ;; Perhaps keep track over the overlay we started from? That would
670 ;; break when the user moved manually, though.
671 (when (and browse-kill-ring-highlight-current-entry
672 (overlays-at (point)))
673 (let ((overs (overlay-lists))
674 (current-overlay (car (overlays-at (point)))))
675 (mapcar #'(lambda (o)
676 (overlay-put o 'face nil))
677 (nconc (car overs) (cdr overs)))
678 (overlay-put current-overlay 'face 'highlight)))
679 (when browse-kill-ring-recenter
680 (recenter 1)))
681
682(defun browse-kill-ring-previous (&optional arg)
683 "Move backward by ARG `kill-ring' entries."
684 (interactive "p")
685 (browse-kill-ring-forward (- arg)))
686
687(defun browse-kill-ring-read-regexp (msg)
688 (let* ((default (car regexp-history))
689 (input
690 (read-from-minibuffer
691 (if default
692 (format "%s for regexp (default `%s'): "
693 msg
694 default)
695 (format "%s (regexp): " msg))
696 nil
697 nil
698 nil
699 'regexp-history)))
700 (if (equal input "")
701 default
702 input)))
703
704(defun browse-kill-ring-search-forward (regexp &optional backwards)
705 "Move to the next `kill-ring' entry matching REGEXP from point.
706If optional arg BACKWARDS is non-nil, move to the previous matching
707entry."
708 (interactive
709 (list (browse-kill-ring-read-regexp "Search forward")
710 current-prefix-arg))
711 (let ((orig (point)))
712 (browse-kill-ring-forward (if backwards -1 1))
713 (let ((overs (overlays-at (point))))
714 (while (and overs
715 (not (if backwards (bobp) (eobp)))
716 (not (string-match regexp
717 (overlay-get (car overs)
718 'browse-kill-ring-target))))
719 (browse-kill-ring-forward (if backwards -1 1))
720 (setq overs (overlays-at (point))))
721 (unless (and overs
722 (string-match regexp
723 (overlay-get (car overs)
724 'browse-kill-ring-target)))
725 (progn
726 (goto-char orig)
727 (message "No more `kill-ring' entries matching %s" regexp))))))
728
729(defun browse-kill-ring-search-backward (regexp)
730 "Move to the previous `kill-ring' entry matching REGEXP from point."
731 (interactive
732 (list (browse-kill-ring-read-regexp "Search backward")))
733 (browse-kill-ring-search-forward regexp t))
734
735(defun browse-kill-ring-quit ()
736 "Take the action specified by `browse-kill-ring-quit-action'."
737 (interactive)
738 (case browse-kill-ring-quit-action
739 (save-and-restore
740 (let (buf (current-buffer))
741 (set-window-configuration browse-kill-ring-original-window-config)
742 (kill-buffer buf)))
743 (kill-and-delete-window
744 (kill-buffer (current-buffer))
745 (unless (= (count-windows) 1)
746 (delete-window)))
747 (bury-and-delete-window
748 (bury-buffer)
749 (unless (= (count-windows) 1)
750 (delete-window)))
751 (t
752 (funcall browse-kill-ring-quit-action))))
753
754(put 'browse-kill-ring-mode 'mode-class 'special)
755(define-derived-mode browse-kill-ring-mode fundamental-mode
756 "Kill Ring"
757 "A major mode for browsing the `kill-ring'.
758You most likely do not want to call `browse-kill-ring-mode' directly; use
759`browse-kill-ring' instead.
760
761\\{browse-kill-ring-mode-map}"
762 (set (make-local-variable 'font-lock-defaults)
763 '(nil t nil nil nil
764 (font-lock-fontify-region-function . browse-kill-ring-fontify-region)))
765 (define-key browse-kill-ring-mode-map (kbd "q") 'browse-kill-ring-quit)
766 (define-key browse-kill-ring-mode-map (kbd "U") 'browse-kill-ring-undo-other-window)
767 (define-key browse-kill-ring-mode-map (kbd "d") 'browse-kill-ring-delete)
768 (define-key browse-kill-ring-mode-map (kbd "s") 'browse-kill-ring-search-forward)
769 (define-key browse-kill-ring-mode-map (kbd "r") 'browse-kill-ring-search-backward)
770 (define-key browse-kill-ring-mode-map (kbd "g") 'browse-kill-ring-update)
771 (define-key browse-kill-ring-mode-map (kbd "l") 'browse-kill-ring-occur)
772 (define-key browse-kill-ring-mode-map (kbd "e") 'browse-kill-ring-edit)
773 (define-key browse-kill-ring-mode-map (kbd "n") 'browse-kill-ring-forward)
774 (define-key browse-kill-ring-mode-map (kbd "p") 'browse-kill-ring-previous)
775 (define-key browse-kill-ring-mode-map [(mouse-2)] 'browse-kill-ring-mouse-insert)
776 (define-key browse-kill-ring-mode-map (kbd "?") 'describe-mode)
777 (define-key browse-kill-ring-mode-map (kbd "h") 'describe-mode)
778 (define-key browse-kill-ring-mode-map (kbd "y") 'browse-kill-ring-insert)
779 (define-key browse-kill-ring-mode-map (kbd "u") 'browse-kill-ring-insert-move-and-quit)
780 (define-key browse-kill-ring-mode-map (kbd "i") 'browse-kill-ring-insert)
781 (define-key browse-kill-ring-mode-map (kbd "o") 'browse-kill-ring-insert-and-move)
782 (define-key browse-kill-ring-mode-map (kbd "x") 'browse-kill-ring-insert-and-delete)
783 (define-key browse-kill-ring-mode-map (kbd "RET") 'browse-kill-ring-insert-and-quit)
784 (define-key browse-kill-ring-mode-map (kbd "b") 'browse-kill-ring-prepend-insert)
785 (define-key browse-kill-ring-mode-map (kbd "a") 'browse-kill-ring-append-insert))
786
787;;;###autoload
788(defun browse-kill-ring-default-keybindings ()
789 "Set up M-y (`yank-pop') so that it can invoke `browse-kill-ring'.
790Normally, if M-y was not preceeded by C-y, then it has no useful
791behavior. This function sets things up so that M-y will invoke
792`browse-kill-ring'."
793 (interactive)
794 (defadvice yank-pop (around kill-ring-browse-maybe (arg))
795 "If last action was not a yank, run `browse-kill-ring' instead."
796 ;; yank-pop has an (interactive "*p") form which does not allow
797 ;; it to run in a read-only buffer. We want browse-kill-ring to
798 ;; be allowed to run in a read only buffer, so we change the
799 ;; interactive form here. In that case, we need to
800 ;; barf-if-buffer-read-only if we're going to call yank-pop with
801 ;; ad-do-it
802 (interactive "p")
803 (if (not (eq last-command 'yank))
804 (browse-kill-ring)
805 (barf-if-buffer-read-only)
806 ad-do-it))
807 (ad-activate 'yank-pop))
808
809(define-derived-mode browse-kill-ring-edit-mode fundamental-mode
810 "Kill Ring Edit"
811 "A major mode for editing a `kill-ring' entry.
812You most likely do not want to call `browse-kill-ring-edit-mode'
813directly; use `browse-kill-ring' instead.
814
815\\{browse-kill-ring-edit-mode-map}"
816 (define-key browse-kill-ring-edit-mode-map (kbd "C-c C-c")
817 'browse-kill-ring-edit-finish))
818
819(defvar browse-kill-ring-edit-target nil)
820(make-variable-buffer-local 'browse-kill-ring-edit-target)
821
822(defun browse-kill-ring-edit ()
823 "Edit the `kill-ring' entry at point."
824 (interactive)
825 (let ((overs (overlays-at (point))))
826 (unless overs
827 (error "No kill ring entry here"))
828 (let* ((target (overlay-get (car overs)
829 'browse-kill-ring-target))
830 (target-cell (member target kill-ring)))
831 (unless target-cell
832 (error "Item deleted from the kill-ring"))
833 (switch-to-buffer (get-buffer-create "*Kill Ring Edit*"))
834 (setq buffer-read-only nil)
835 (erase-buffer)
836 (insert target)
837 (goto-char (point-min))
838 (browse-kill-ring-resize-window)
839 (browse-kill-ring-edit-mode)
840 (message "%s"
841 (substitute-command-keys
842 "Use \\[browse-kill-ring-edit-finish] to finish editing."))
843 (setq browse-kill-ring-edit-target target-cell))))
844
845(defun browse-kill-ring-edit-finish ()
846 "Commit the changes to the `kill-ring'."
847 (interactive)
848 (if browse-kill-ring-edit-target
849 (setcar browse-kill-ring-edit-target (buffer-string))
850 (when (y-or-n-p "The item has been deleted; add to front? ")
851 (push (buffer-string) kill-ring)))
852 (bury-buffer)
853 ;; The user might have rearranged the windows
854 (when (eq major-mode 'browse-kill-ring-mode)
855 (browse-kill-ring-setup (current-buffer)
856 browse-kill-ring-original-window
857 nil
858 browse-kill-ring-original-window-config)
859 (browse-kill-ring-resize-window)))
860
861(defmacro browse-kill-ring-add-overlays-for (item &rest body)
862 (let ((beg (gensym "browse-kill-ring-add-overlays-"))
863 (end (gensym "browse-kill-ring-add-overlays-")))
864 `(let ((,beg (point))
865 (,end
866 (progn
867 ,@body
868 (point))))
869 (let ((o (make-overlay ,beg ,end)))
870 (overlay-put o 'browse-kill-ring-target ,item)
871 (overlay-put o 'mouse-face 'highlight)))))
872;; (put 'browse-kill-ring-add-overlays-for 'lisp-indent-function 1)
873
874(defun browse-kill-ring-elide (str)
875 (if (and browse-kill-ring-maximum-display-length
876 (> (length str)
877 browse-kill-ring-maximum-display-length))
878 (concat (substring str 0 (- browse-kill-ring-maximum-display-length 3))
879 (browse-kill-ring-propertize "..." 'browse-kill-ring-extra t))
880 str))
881
882(defun browse-kill-ring-insert-as-one-line (items)
883 (dolist (item items)
884 (browse-kill-ring-add-overlays-for item
885 (let* ((item (browse-kill-ring-elide item))
886 (len (length item))
887 (start 0)
888 (newl (browse-kill-ring-propertize "\\n" 'browse-kill-ring-extra t)))
889 (while (and (< start len)
890 (string-match "\n" item start))
891 (insert (substring item start (match-beginning 0))
892 newl)
893 (setq start (match-end 0)))
894 (insert (substring item start len))))
895 (insert "\n")))
896
897(defun browse-kill-ring-insert-as-separated (items)
898 (while (cdr items)
899 (browse-kill-ring-insert-as-separated-1 (car items) t)
900 (setq items (cdr items)))
901 (when items
902 (browse-kill-ring-insert-as-separated-1 (car items) nil)))
903
904(defun browse-kill-ring-insert-as-separated-1 (origitem separatep)
905 (let* ((item (browse-kill-ring-elide origitem))
906 (len (length item)))
907 (browse-kill-ring-add-overlays-for origitem
908 (insert item))
909 ;; When the kill-ring has items with read-only text property at
910 ;; **the end of** string, browse-kill-ring-setup fails with error
911 ;; `Text is read-only'. So inhibit-read-only here.
912 ;; See http://bugs.debian.org/225082
913 ;; - INOUE Hiroyuki <dombly@kc4.so-net.ne.jp>
914 (let ((inhibit-read-only t))
915 (insert "\n")
916 (when separatep
917 (insert (browse-kill-ring-propertize browse-kill-ring-separator
918 'browse-kill-ring-extra t
919 'browse-kill-ring-separator t))
920 (insert "\n")))))
921
922(defun browse-kill-ring-occur (regexp)
923 "Display all `kill-ring' entries matching REGEXP."
924 (interactive
925 (list
926 (browse-kill-ring-read-regexp "Display kill ring entries matching")))
927 (assert (eq major-mode 'browse-kill-ring-mode))
928 (browse-kill-ring-setup (current-buffer)
929 browse-kill-ring-original-window
930 regexp)
931 (browse-kill-ring-resize-window))
932
933(defun browse-kill-ring-fontify-on-property (prop face beg end)
934 (save-excursion
935 (goto-char beg)
936 (let ((prop-end nil))
937 (while
938 (setq prop-end
939 (let ((prop-beg (or (and (get-text-property (point) prop) (point))
940 (next-single-property-change (point) prop nil end))))
941 (when (and prop-beg (not (= prop-beg end)))
942 (let ((prop-end (next-single-property-change prop-beg prop nil end)))
943 (when (and prop-end (not (= prop-end end)))
944 (put-text-property prop-beg prop-end 'face face)
945 prop-end)))))
946 (goto-char prop-end)))))
947
948(defun browse-kill-ring-fontify-region (beg end &optional verbose)
949 (when verbose (message "Fontifying..."))
950 (let ((buffer-read-only nil))
951 (browse-kill-ring-fontify-on-property 'browse-kill-ring-extra 'bold beg end)
952 (browse-kill-ring-fontify-on-property 'browse-kill-ring-separator
953 browse-kill-ring-separator-face beg end))
954 (when verbose (message "Fontifying...done")))
955
956(defun browse-kill-ring-update ()
957 "Update the buffer to reflect outside changes to `kill-ring'."
958 (interactive)
959 (assert (eq major-mode 'browse-kill-ring-mode))
960 (browse-kill-ring-setup (current-buffer)
961 browse-kill-ring-original-window)
962 (browse-kill-ring-resize-window))
963
964(defun browse-kill-ring-setup (buf window &optional regexp window-config)
965 (with-current-buffer buf
966 (unwind-protect
967 (progn
968 (browse-kill-ring-mode)
969 (setq buffer-read-only nil)
970 (when (eq browse-kill-ring-display-style
971 'one-line)
972 (setq truncate-lines t))
973 (let ((inhibit-read-only t))
974 (erase-buffer))
975 (setq browse-kill-ring-original-window window
976 browse-kill-ring-original-window-config
977 (or window-config
978 (current-window-configuration)))
979 (let ((browse-kill-ring-maximum-display-length
980 (if (and browse-kill-ring-maximum-display-length
981 (<= browse-kill-ring-maximum-display-length 3))
982 4
983 browse-kill-ring-maximum-display-length))
984 (items (mapcar
985 (if browse-kill-ring-depropertize
986 #'browse-kill-ring-depropertize-string
987 #'copy-sequence)
988 kill-ring)))
989 (when (not browse-kill-ring-display-duplicates)
990 ;; I'm not going to rewrite `delete-duplicates'. If
991 ;; someone really wants to rewrite it here, send me a
992 ;; patch.
993 (require 'cl)
994 (setq items (delete-duplicates items :test #'equal)))
995 (when (stringp regexp)
996 (setq items (delq nil
997 (mapcar
998 #'(lambda (item)
999 (when (string-match regexp item)
1000 item))
1001 items))))
1002 (funcall (or (cdr (assq browse-kill-ring-display-style
1003 browse-kill-ring-display-styles))
1004 (error "Invalid `browse-kill-ring-display-style': %s"
1005 browse-kill-ring-display-style))
1006 items)
1007;; Code from Michael Slass <mikesl@wrq.com>
1008 (message
1009 (let ((entry (if (= 1 (length kill-ring)) "entry" "entries")))
1010 (concat
1011 (if (and (not regexp)
1012 browse-kill-ring-display-duplicates)
1013 (format "%s %s in the kill ring."
1014 (length kill-ring) entry)
1015 (format "%s (of %s) %s in the kill ring shown."
1016 (length items) (length kill-ring) entry))
1017 (substitute-command-keys
1018 (concat " Type \\[browse-kill-ring-quit] to quit. "
1019 "\\[describe-mode] for help.")))))
1020;; End code from Michael Slass <mikesl@wrq.com>
1021 (set-buffer-modified-p nil)
1022 (goto-char (point-min))
1023 (browse-kill-ring-forward 0)
1024 (when regexp
1025 (setq mode-name (concat "Kill Ring [" regexp "]")))
1026 (run-hooks 'browse-kill-ring-hook)
1027 ;; I will be very glad when I can get rid of this gross
1028 ;; hack, which solely exists for XEmacs users.
1029 (when (and (featurep 'xemacs)
1030 font-lock-mode)
1031 (browse-kill-ring-fontify-region (point-min) (point-max)))))
1032 (progn
1033 (setq buffer-read-only t)))))
1034
1035;;;###autoload
1036(defun browse-kill-ring ()
1037 "Display items in the `kill-ring' in another buffer."
1038 (interactive)
1039 (if (eq major-mode 'browse-kill-ring-mode)
1040 (message "Already viewing the kill ring")
1041 (let ((orig-buf (current-buffer))
1042 (buf (get-buffer-create "*Kill Ring*")))
1043 (browse-kill-ring-setup buf (selected-window))
1044 (pop-to-buffer buf)
1045 (browse-kill-ring-resize-window)
1046 nil)))
1047
1048(provide 'browse-kill-ring)
1049
1050;;; browse-kill-ring.el ends here