]> code.delx.au - gnu-emacs/blobdiff - lisp/register.el
Fix assertion violations in try_window_id (Bug#19511)
[gnu-emacs] / lisp / register.el
index 97b6eb0dfc8349e33acd6b799aec4f3573d2e061..b8fe613f9214ed7c0f4c9e14a275e80fd4d85019 100644 (file)
@@ -1,9 +1,9 @@
-;;; register.el --- register commands for Emacs
+;;; register.el --- register commands for Emacs      -*- lexical-binding: t; -*-
 
 
-;; Copyright (C) 1985, 1993, 1994, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1993-1994, 2001-2015 Free Software Foundation,
+;; Inc.
 
 
-;; Maintainer: FSF
+;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: internal
 ;; Package: emacs
 
 ;; Keywords: internal
 ;; Package: emacs
 
 ;; pieces of buffer state to named variables.  The entry points are
 ;; documented in the Emacs user's manual.
 
 ;; pieces of buffer state to named variables.  The entry points are
 ;; documented in the Emacs user's manual.
 
-(declare-function semantic-insert-foreign-tag "semantic/tag" (foreign-tag))
-(declare-function semantic-tag-buffer "semantic/tag" (tag))
-(declare-function semantic-tag-start "semantic/tag" (tag))
-
-;;; Global key bindings
-
-(define-key ctl-x-r-map "\C-@" 'point-to-register)
-(define-key ctl-x-r-map [?\C-\ ] 'point-to-register)
-(define-key ctl-x-r-map " " 'point-to-register)
-(define-key ctl-x-r-map "j" 'jump-to-register)
-(define-key ctl-x-r-map "s" 'copy-to-register)
-(define-key ctl-x-r-map "x" 'copy-to-register)
-(define-key ctl-x-r-map "i" 'insert-register)
-(define-key ctl-x-r-map "g" 'insert-register)
-(define-key ctl-x-r-map "r" 'copy-rectangle-to-register)
-(define-key ctl-x-r-map "n" 'number-to-register)
-(define-key ctl-x-r-map "+" 'increment-register)
-(define-key ctl-x-r-map "w" 'window-configuration-to-register)
-(define-key ctl-x-r-map "f" 'frame-configuration-to-register)
+(eval-when-compile (require 'cl-lib))
 
 ;;; Code:
 
 
 ;;; Code:
 
+(cl-defstruct
+  (registerv (:constructor nil)
+            (:constructor registerv--make (&optional data print-func
+                                                     jump-func insert-func))
+            (:copier nil)
+            (:type vector)
+            :named)
+  (data        nil :read-only t)
+  (print-func  nil :read-only t)
+  (jump-func   nil :read-only t)
+  (insert-func nil :read-only t))
+
+(cl-defun registerv-make (data &key print-func jump-func insert-func)
+  "Create a register value object.
+
+DATA can be any value.
+PRINT-FUNC if provided controls how `list-registers' and
+`view-register' print the register.  It should be a function
+receiving one argument DATA and print text that completes
+this sentence:
+  Register X contains [TEXT PRINTED BY PRINT-FUNC]
+JUMP-FUNC if provided, controls how `jump-to-register' jumps to the register.
+INSERT-FUNC if provided, controls how `insert-register' insert the register.
+They both receive DATA as argument."
+  (registerv--make data print-func jump-func insert-func))
+
 (defvar register-alist nil
   "Alist of elements (NAME . CONTENTS), one for each Emacs register.
 (defvar register-alist nil
   "Alist of elements (NAME . CONTENTS), one for each Emacs register.
-NAME is a character (a number).  CONTENTS is a string, number, marker or list.
+NAME is a character (a number).  CONTENTS is a string, number, marker, list
+or a struct returned by `registerv-make'.
 A list of strings represents a rectangle.
 A list of the form (file . FILE-NAME) represents the file named FILE-NAME.
 A list of the form (file-query FILE-NAME POSITION) represents
 A list of strings represents a rectangle.
 A list of the form (file . FILE-NAME) represents the file named FILE-NAME.
 A list of the form (file-query FILE-NAME POSITION) represents
@@ -64,6 +73,29 @@ A list of the form (WINDOW-CONFIGURATION POSITION)
 A list of the form (FRAME-CONFIGURATION POSITION)
  represents a saved frame configuration plus a saved value of point.")
 
 A list of the form (FRAME-CONFIGURATION POSITION)
  represents a saved frame configuration plus a saved value of point.")
 
+(defgroup register nil
+  "Register commands."
+  :group 'convenience
+  :version "24.3")
+
+(defcustom register-separator nil
+  "Register containing the text to put between collected texts, or nil if none.
+
+When collecting text with \\[append-to-register] (or \\[prepend-to-register]),
+contents of this register is added to the beginning (or end, respectively)
+of the marked text."
+  :group 'register
+  :type '(choice (const :tag "None" nil)
+                (character :tag "Use register" :value ?+)))
+
+(defcustom register-preview-delay 1
+  "If non-nil, time to wait in seconds before popping up a preview window.
+If nil, do not show register previews, unless `help-char' (or a member of
+`help-event-list') is pressed."
+  :version "24.4"
+  :type '(choice number (const :tag "No preview unless requested" nil))
+  :group 'register)
+
 (defun get-register (register)
   "Return contents of Emacs register named REGISTER, or nil if none."
   (cdr (assq register register-alist)))
 (defun get-register (register)
   "Return contents of Emacs register named REGISTER, or nil if none."
   (cdr (assq register register-alist)))
@@ -77,50 +109,142 @@ See the documentation of the variable `register-alist' for possible VALUEs."
       (push (cons register value) register-alist))
     value))
 
       (push (cons register value) register-alist))
     value))
 
+(defun register-describe-oneline (c)
+  "One-line description of register C."
+  (let ((d (replace-regexp-in-string
+            "\n[ \t]*" " "
+            (with-output-to-string (describe-register-1 c)))))
+    (if (string-match "Register.+? contains \\(?:an? \\|the \\)?" d)
+        (substring d (match-end 0))
+      d)))
+
+(defun register-preview-default (r)
+  "Default function for the variable `register-preview-function'."
+  (format "%s: %s\n"
+         (single-key-description (car r))
+         (register-describe-oneline (car r))))
+
+(defvar register-preview-function #'register-preview-default
+  "Function to format a register for previewing.
+Takes one argument, a cons (NAME . CONTENTS) as found in `register-alist'.
+Returns a string.")
+
+(defun register-preview (buffer &optional show-empty)
+  "Pop up a window to show register preview in BUFFER.
+If SHOW-EMPTY is non-nil show the window even if no registers.
+Format of each entry is controlled by the variable `register-preview-function'."
+  (when (or show-empty (consp register-alist))
+    (with-current-buffer-window
+     buffer
+     (cons 'display-buffer-below-selected
+          '((window-height . fit-window-to-buffer)))
+     nil
+     (with-current-buffer standard-output
+       (setq cursor-in-non-selected-windows nil)
+       (insert (mapconcat register-preview-function register-alist ""))))))
+
+(defun register-read-with-preview (prompt)
+  "Read and return a register name, possibly showing existing registers.
+Prompt with the string PROMPT.  If `register-alist' and
+`register-preview-delay' are both non-nil, display a window
+listing existing registers after `register-preview-delay' seconds.
+If `help-char' (or a member of `help-event-list') is pressed,
+display such a window regardless."
+  (let* ((buffer "*Register Preview*")
+        (timer (when (numberp register-preview-delay)
+                 (run-with-timer register-preview-delay nil
+                                 (lambda ()
+                                   (unless (get-buffer-window buffer)
+                                     (register-preview buffer))))))
+        (help-chars (cl-loop for c in (cons help-char help-event-list)
+                             when (not (get-register c))
+                             collect c)))
+    (unwind-protect
+       (progn
+         (while (memq (read-event (propertize prompt 'face 'minibuffer-prompt))
+                      help-chars)
+           (unless (get-buffer-window buffer)
+             (register-preview buffer 'show-empty)))
+         (if (characterp last-input-event) last-input-event
+           (error "Non-character input-event")))
+      (and (timerp timer) (cancel-timer timer))
+      (let ((w (get-buffer-window buffer)))
+        (and (window-live-p w) (delete-window w)))
+      (and (get-buffer buffer) (kill-buffer buffer)))))
+
 (defun point-to-register (register &optional arg)
   "Store current location of point in register REGISTER.
 With prefix argument, store current frame configuration.
 Use \\[jump-to-register] to go to that location or restore that configuration.
 (defun point-to-register (register &optional arg)
   "Store current location of point in register REGISTER.
 With prefix argument, store current frame configuration.
 Use \\[jump-to-register] to go to that location or restore that configuration.
-Argument is a character, naming the register."
-  (interactive "cPoint to register: \nP")
+Argument is a character, naming the register.
+
+Interactively, reads the register using `register-read-with-preview'."
+  (interactive (list (register-read-with-preview "Point to register: ")
+                    current-prefix-arg))
   ;; Turn the marker into a file-ref if the buffer is killed.
   (add-hook 'kill-buffer-hook 'register-swap-out nil t)
   (set-register register
                (if arg (list (current-frame-configuration) (point-marker))
                  (point-marker))))
 
   ;; Turn the marker into a file-ref if the buffer is killed.
   (add-hook 'kill-buffer-hook 'register-swap-out nil t)
   (set-register register
                (if arg (list (current-frame-configuration) (point-marker))
                  (point-marker))))
 
-(defun window-configuration-to-register (register &optional arg)
+(defun window-configuration-to-register (register &optional _arg)
   "Store the window configuration of the selected frame in register REGISTER.
 Use \\[jump-to-register] to restore the configuration.
   "Store the window configuration of the selected frame in register REGISTER.
 Use \\[jump-to-register] to restore the configuration.
-Argument is a character, naming the register."
-  (interactive "cWindow configuration to register: \nP")
+Argument is a character, naming the register.
+
+Interactively, reads the register using `register-read-with-preview'."
+  (interactive (list (register-read-with-preview
+                     "Window configuration to register: ")
+                    current-prefix-arg))
   ;; current-window-configuration does not include the value
   ;; of point in the current buffer, so record that separately.
   (set-register register (list (current-window-configuration) (point-marker))))
 
   ;; current-window-configuration does not include the value
   ;; of point in the current buffer, so record that separately.
   (set-register register (list (current-window-configuration) (point-marker))))
 
-(defun frame-configuration-to-register (register &optional arg)
+;; It has had the optional arg for ages, but never used it.
+(set-advertised-calling-convention 'window-configuration-to-register
+                                  '(register) "24.4")
+
+(defun frame-configuration-to-register (register &optional _arg)
   "Store the window configuration of all frames in register REGISTER.
 Use \\[jump-to-register] to restore the configuration.
   "Store the window configuration of all frames in register REGISTER.
 Use \\[jump-to-register] to restore the configuration.
-Argument is a character, naming the register."
-  (interactive "cFrame configuration to register: \nP")
+Argument is a character, naming the register.
+
+Interactively, reads the register using `register-read-with-preview'."
+  (interactive (list (register-read-with-preview
+                     "Frame configuration to register: ")
+                    current-prefix-arg))
   ;; current-frame-configuration does not include the value
   ;; of point in the current buffer, so record that separately.
   (set-register register (list (current-frame-configuration) (point-marker))))
 
   ;; current-frame-configuration does not include the value
   ;; of point in the current buffer, so record that separately.
   (set-register register (list (current-frame-configuration) (point-marker))))
 
+;; It has had the optional arg for ages, but never used it.
+(set-advertised-calling-convention 'frame-configuration-to-register
+                                  '(register) "24.4")
+
+(make-obsolete 'frame-configuration-to-register 'frameset-to-register' "24.4")
+
 (defalias 'register-to-point 'jump-to-register)
 (defun jump-to-register (register &optional delete)
   "Move point to location stored in a register.
 If the register contains a file name, find that file.
 \(To put a file name in a register, you must use `set-register'.)
 (defalias 'register-to-point 'jump-to-register)
 (defun jump-to-register (register &optional delete)
   "Move point to location stored in a register.
 If the register contains a file name, find that file.
 \(To put a file name in a register, you must use `set-register'.)
-If the register contains a window configuration (one frame) or a frame
-configuration (all frames), restore that frame or all frames accordingly.
+If the register contains a window configuration (one frame) or a frameset
+\(all frames), restore that frame or all frames accordingly.
 First argument is a character, naming the register.
 Optional second arg non-nil (interactively, prefix argument) says to
 First argument is a character, naming the register.
 Optional second arg non-nil (interactively, prefix argument) says to
-delete any existing frames that the frame configuration doesn't mention.
-\(Otherwise, these frames are iconified.)"
-  (interactive "cJump to register: \nP")
+delete any existing frames that the frameset doesn't mention.
+\(Otherwise, these frames are iconified.)
+
+Interactively, reads the register using `register-read-with-preview'."
+  (interactive (list (register-read-with-preview "Jump to register: ")
+                    current-prefix-arg))
   (let ((val (get-register register)))
     (cond
   (let ((val (get-register register)))
     (cond
+     ((registerv-p val)
+      (cl-assert (registerv-jump-func val) nil
+              "Don't know how to jump to register %s"
+              (single-key-description register))
+      (funcall (registerv-jump-func val) (registerv-data val)))
      ((and (consp val) (frame-configuration-p (car val)))
       (set-frame-configuration (car val) (not delete))
       (goto-char (cadr val)))
      ((and (consp val) (frame-configuration-p (car val)))
       (set-frame-configuration (car val) (not delete))
       (goto-char (cadr val)))
@@ -140,11 +264,6 @@ delete any existing frames that the frame configuration doesn't mention.
          (error "Register access aborted"))
       (find-file (nth 1 val))
       (goto-char (nth 2 val)))
          (error "Register access aborted"))
       (find-file (nth 1 val))
       (goto-char (nth 2 val)))
-     ((and (fboundp 'semantic-foreign-tag-p)
-          semantic-mode
-          (semantic-foreign-tag-p val))
-      (switch-to-buffer (semantic-tag-buffer val))
-      (goto-char (semantic-tag-start val)))
      (t
       (error "Register doesn't contain a buffer position or configuration")))))
 
      (t
       (error "Register doesn't contain a buffer position or configuration")))))
 
@@ -164,8 +283,11 @@ delete any existing frames that the frame configuration doesn't mention.
 Two args, NUMBER and REGISTER (a character, naming the register).
 If NUMBER is nil, a decimal number is read from the buffer starting
 at point, and point moves to the end of that number.
 Two args, NUMBER and REGISTER (a character, naming the register).
 If NUMBER is nil, a decimal number is read from the buffer starting
 at point, and point moves to the end of that number.
-Interactively, NUMBER is the prefix arg (none means nil)."
-  (interactive "P\ncNumber to register: ")
+Interactively, NUMBER is the prefix arg (none means nil).
+
+Interactively, reads the register using `register-read-with-preview'."
+  (interactive (list current-prefix-arg
+                    (register-read-with-preview "Number to register: ")))
   (set-register register
                (if number
                    (prefix-numeric-value number)
   (set-register register
                (if number
                    (prefix-numeric-value number)
@@ -175,18 +297,34 @@ Interactively, NUMBER is the prefix arg (none means nil)."
                        (string-to-number (match-string 0)))
                    0))))
 
                        (string-to-number (match-string 0)))
                    0))))
 
-(defun increment-register (number register)
-  "Add NUMBER to the contents of register REGISTER.
-Interactively, NUMBER is the prefix arg."
-  (interactive "p\ncIncrement register: ")
-  (or (numberp (get-register register))
-      (error "Register does not contain a number"))
-  (set-register register (+ number (get-register register))))
+(defun increment-register (prefix register)
+  "Augment contents of REGISTER.
+Interactively, PREFIX is in raw form.
+
+If REGISTER contains a number, add `prefix-numeric-value' of
+PREFIX to it.
+
+If REGISTER is empty or if it contains text, call
+`append-to-register' with `delete-flag' set to PREFIX.
+
+Interactively, reads the register using `register-read-with-preview'."
+  (interactive (list current-prefix-arg
+                    (register-read-with-preview "Increment register: ")))
+  (let ((register-val (get-register register)))
+    (cond
+     ((numberp register-val)
+      (let ((number (prefix-numeric-value prefix)))
+       (set-register register (+ number register-val))))
+     ((or (not register-val) (stringp register-val))
+      (append-to-register register (region-beginning) (region-end) prefix))
+     (t (error "Register does not contain a number or text")))))
 
 (defun view-register (register)
   "Display what is contained in register named REGISTER.
 
 (defun view-register (register)
   "Display what is contained in register named REGISTER.
-The Lisp value REGISTER is a character."
-  (interactive "cView register: ")
+The Lisp value REGISTER is a character.
+
+Interactively, reads the register using `register-read-with-preview'."
+  (interactive (list (register-read-with-preview "View register: ")))
   (let ((val (get-register register)))
     (if (null val)
        (message "Register %s is empty" (single-key-description register))
   (let ((val (get-register register)))
     (if (null val)
        (message "Register %s is empty" (single-key-description register))
@@ -210,6 +348,11 @@ The Lisp value REGISTER is a character."
   (princ " contains ")
   (let ((val (get-register register)))
     (cond
   (princ " contains ")
   (let ((val (get-register register)))
     (cond
+     ((registerv-p val)
+      (if (registerv-print-func val)
+          (funcall (registerv-print-func val) (registerv-data val))
+        (princ "[UNPRINTABLE CONTENTS].")))
+
      ((numberp val)
       (princ val))
 
      ((numberp val)
       (princ val))
 
@@ -253,6 +396,7 @@ The Lisp value REGISTER is a character."
        (princ (car val))))
 
      ((stringp val)
        (princ (car val))))
 
      ((stringp val)
+      (setq val (copy-sequence val))
       (if (eq yank-excluded-properties t)
          (set-text-properties 0 (length val) nil val)
        (remove-list-of-text-properties 0 (length val)
       (if (eq yank-excluded-properties t)
          (set-text-properties 0 (length val) nil val)
        (remove-list-of-text-properties 0 (length val)
@@ -281,11 +425,21 @@ The Lisp value REGISTER is a character."
   "Insert contents of register REGISTER.  (REGISTER is a character.)
 Normally puts point before and mark after the inserted text.
 If optional second arg is non-nil, puts mark before and point after.
   "Insert contents of register REGISTER.  (REGISTER is a character.)
 Normally puts point before and mark after the inserted text.
 If optional second arg is non-nil, puts mark before and point after.
-Interactively, second arg is non-nil if prefix arg is supplied."
-  (interactive "*cInsert register: \nP")
+Interactively, second arg is non-nil if prefix arg is supplied.
+
+Interactively, reads the register using `register-read-with-preview'."
+  (interactive (progn
+                (barf-if-buffer-read-only)
+                (list (register-read-with-preview "Insert register: ")
+                      current-prefix-arg)))
   (push-mark)
   (let ((val (get-register register)))
     (cond
   (push-mark)
   (let ((val (get-register register)))
     (cond
+     ((registerv-p val)
+      (cl-assert (registerv-insert-func val) nil
+              "Don't know how to insert register %s"
+              (single-key-description register))
+      (funcall (registerv-insert-func val) (registerv-data val)))
      ((consp val)
       (insert-rectangle val))
      ((stringp val)
      ((consp val)
       (insert-rectangle val))
      ((stringp val)
@@ -294,50 +448,80 @@ Interactively, second arg is non-nil if prefix arg is supplied."
       (princ val (current-buffer)))
      ((and (markerp val) (marker-position val))
       (princ (marker-position val) (current-buffer)))
       (princ val (current-buffer)))
      ((and (markerp val) (marker-position val))
       (princ (marker-position val) (current-buffer)))
-     ((and (fboundp 'semantic-foreign-tag-p)
-          semantic-mode
-          (semantic-foreign-tag-p val))
-      (semantic-insert-foreign-tag val))
      (t
       (error "Register does not contain text"))))
   (if (not arg) (exchange-point-and-mark)))
 
      (t
       (error "Register does not contain text"))))
   (if (not arg) (exchange-point-and-mark)))
 
-(defun copy-to-register (register start end &optional delete-flag)
+(defun copy-to-register (register start end &optional delete-flag region)
   "Copy region into register REGISTER.
 With prefix arg, delete as well.
   "Copy region into register REGISTER.
 With prefix arg, delete as well.
-Called from program, takes four args: REGISTER, START, END and DELETE-FLAG.
-START and END are buffer positions indicating what to copy."
-  (interactive "cCopy to register: \nr\nP")
-  (set-register register (filter-buffer-substring start end))
-  (if delete-flag (delete-region start end)))
+Called from program, takes five args: REGISTER, START, END, DELETE-FLAG,
+and REGION.  START and END are buffer positions indicating what to copy.
+The optional argument REGION if non-nil, indicates that we're not just
+copying some text between START and END, but we're copying the region.
+
+Interactively, reads the register using `register-read-with-preview'."
+  (interactive (list (register-read-with-preview "Copy to register: ")
+                    (region-beginning)
+                    (region-end)
+                    current-prefix-arg
+                    t))
+  (set-register register (if region
+                            (funcall region-extract-function delete-flag)
+                          (prog1 (filter-buffer-substring start end)
+                            (if delete-flag (delete-region start end)))))
+  (setq deactivate-mark t)
+  (cond (delete-flag)
+       ((called-interactively-p 'interactive)
+        (indicate-copied-region))))
 
 (defun append-to-register (register start end &optional delete-flag)
   "Append region to text in register REGISTER.
 With prefix arg, delete as well.
 Called from program, takes four args: REGISTER, START, END and DELETE-FLAG.
 
 (defun append-to-register (register start end &optional delete-flag)
   "Append region to text in register REGISTER.
 With prefix arg, delete as well.
 Called from program, takes four args: REGISTER, START, END and DELETE-FLAG.
-START and END are buffer positions indicating what to append."
-  (interactive "cAppend to register: \nr\nP")
+START and END are buffer positions indicating what to append.
+
+Interactively, reads the register using `register-read-with-preview'."
+  (interactive (list (register-read-with-preview "Append to register: ")
+                    (region-beginning)
+                    (region-end)
+                    current-prefix-arg))
   (let ((reg (get-register register))
   (let ((reg (get-register register))
-        (text (filter-buffer-substring start end)))
+        (text (filter-buffer-substring start end))
+       (separator (and register-separator (get-register register-separator))))
     (set-register
      register (cond ((not reg) text)
     (set-register
      register (cond ((not reg) text)
-                    ((stringp reg) (concat reg text))
+                    ((stringp reg) (concat reg separator text))
                     (t (error "Register does not contain text")))))
                     (t (error "Register does not contain text")))))
-  (if delete-flag (delete-region start end)))
+  (setq deactivate-mark t)
+  (cond (delete-flag
+        (delete-region start end))
+       ((called-interactively-p 'interactive)
+        (indicate-copied-region))))
 
 (defun prepend-to-register (register start end &optional delete-flag)
   "Prepend region to text in register REGISTER.
 With prefix arg, delete as well.
 Called from program, takes four args: REGISTER, START, END and DELETE-FLAG.
 
 (defun prepend-to-register (register start end &optional delete-flag)
   "Prepend region to text in register REGISTER.
 With prefix arg, delete as well.
 Called from program, takes four args: REGISTER, START, END and DELETE-FLAG.
-START and END are buffer positions indicating what to prepend."
-  (interactive "cPrepend to register: \nr\nP")
+START and END are buffer positions indicating what to prepend.
+
+Interactively, reads the register using `register-read-with-preview'."
+  (interactive (list (register-read-with-preview "Prepend to register: ")
+                    (region-beginning)
+                    (region-end)
+                    current-prefix-arg))
   (let ((reg (get-register register))
   (let ((reg (get-register register))
-        (text (filter-buffer-substring start end)))
+        (text (filter-buffer-substring start end))
+       (separator (and register-separator (get-register register-separator))))
     (set-register
      register (cond ((not reg) text)
     (set-register
      register (cond ((not reg) text)
-                    ((stringp reg) (concat text reg))
+                    ((stringp reg) (concat text separator reg))
                     (t (error "Register does not contain text")))))
                     (t (error "Register does not contain text")))))
-  (if delete-flag (delete-region start end)))
+  (setq deactivate-mark t)
+  (cond (delete-flag
+        (delete-region start end))
+       ((called-interactively-p 'interactive)
+        (indicate-copied-region))))
 
 (defun copy-rectangle-to-register (register start end &optional delete-flag)
   "Copy rectangular region into register REGISTER.
 
 (defun copy-rectangle-to-register (register start end &optional delete-flag)
   "Copy rectangular region into register REGISTER.
@@ -345,13 +529,22 @@ With prefix arg, delete as well.
 To insert this register in the buffer, use \\[insert-register].
 
 Called from a program, takes four args: REGISTER, START, END and DELETE-FLAG.
 To insert this register in the buffer, use \\[insert-register].
 
 Called from a program, takes four args: REGISTER, START, END and DELETE-FLAG.
-START and END are buffer positions giving two corners of rectangle."
-  (interactive "cCopy rectangle to register: \nr\nP")
-  (set-register register
-               (if delete-flag
-                   (delete-extract-rectangle start end)
-                 (extract-rectangle start end))))
+START and END are buffer positions giving two corners of rectangle.
+
+Interactively, reads the register using `register-read-with-preview'."
+  (interactive (list (register-read-with-preview
+                     "Copy rectangle to register: ")
+                    (region-beginning)
+                    (region-end)
+                    current-prefix-arg))
+  (let ((rectangle (if delete-flag
+                      (delete-extract-rectangle start end)
+                    (extract-rectangle start end))))
+    (set-register register rectangle)
+    (when (and (null delete-flag)
+              (called-interactively-p 'interactive))
+      (setq deactivate-mark t)
+      (indicate-copied-region (length (car rectangle))))))
 
 (provide 'register)
 
 (provide 'register)
-;; arch-tag: ce14dd68-8265-475f-9341-5d4ec5a53035
 ;;; register.el ends here
 ;;; register.el ends here