Subject: Re: bug#30085: 25.2: Documentation for cua-rectangle-mark-mode

From: Boruch Baum <[email-masked]>
Date: Wed, 21 Mar 2018 08:48:36 -0400

I had some time to look at the pacakge, and produced some notes and a
patch file, before realizing that it would be a good idea to consult
with you both before continuing and possibly wasting a lot of my
volunteer time-budget.

@Eli: In many places in the code, there exist in-line documentation that
would be appropriate as a doc-string; however, those cases are mostly
internal functions of the form `cua--foo', so the question arose for me:
is there emacs policy NOT to produce doc-strings for such functions.
Personally, it's more convenient to my work-flow to be able to use ivy
to get a pop-up docstring for thing-at-point than to open either open a
buffer (only to have to immediately kill it) or ping-pong among places
in a single buffer. So my vote and inclination is make the doc-strings,
but policy is policy.

@Kim & @ELi: My initial notes and patch file are attached, for feedback
if I got anything wrong, and for approval of the proposed changes:

1. The docstring issue, until I realized I should ask.

2. @Kim: At the end of the first diff block, I noted an issue about the
  `cua--last-killed-rectangle' data structure. Could you set me right
   about it?

3. I noticed that `M-m' was bound to `cua-copy-rectangle-as-text'
   instead of `back-to-indentation', so I took the liberty of writing a
   function `cua-resize-rectangle-back-to-indentation' and binding it to
   `M-m', which is what most users would expect. If this approved, to
   what should be bound `cua-copy-rectangle-as-text'

4. Function `cua-resize-rectangle-bot' had a bug in that it always
   placed point at the actual (point-max) even though the rectangle
   corner would not be there. This would occur when (point-max) was at a
   column number smaller than the left edge of the rectangle. The patch
   file includes the fix.

5. Two commonly used navigation functions, normally bound to `C-a' and
   `C-e' were not remapped. (DONE)

6. The help message is remapped from `C-?' to `M-?' for the sanity of
   people like me who use emacs-nox and can only perform a `C-?' by
   typing `C-x @ c ?'.

7. The current keybindings are made using an old method of keystroke
   definition that I find a bit scary. Is it OK / desirable to change
   the method uniformly to use `kbd'?

First slow steps.

-- 
hkp://keys.gnupg.net CA45 09B5 5351 7C11 A9D1 7286 0036 9E45 1595 8BC0
--- -	2018-03-21 08:18:33.833883945 -0400
+++ /home/optimum/cua-rect.el	2018-03-21 07:33:31.822488232 -0400
@@ -37,26 +37,58 @@
 
 (require 'rect)
 
-;; If non-nil, restrict current region to this rectangle.
-;; Value is a vector [top bot left right corner ins virt select].
-;; CORNER specifies currently active corner 0=t/l 1=t/r 2=b/l 3=b/r.
-;; INS specifies whether to insert on left(nil) or right(t) side.
-;; If VIRT is non-nil, virtual straight edges are enabled.
-;; If SELECT is a regexp, only lines starting with that regexp are affected.")
-(defvar cua--rectangle nil)
-(make-variable-buffer-local 'cua--rectangle)
+(defvar cua--rectangle nil
+  "Current cua-rectangle definition.
+
+A cua-rectangle definition is a vector used for all actions in
+`cua-rectangle-mark-mode', of the form:
+
+  [top bot left right corner ins virt select]
+
+TOP is the upper-left corner point.
+
+BOTTOM is the point at the end of line after the the lower-right
+corner point.
+
+LEFT and RIGHT are column numbers.
 
-;; Most recent rectangle geometry.  Note: car is buffer.
-(defvar cua--last-rectangle nil)
+CORNER specifies currently active corner 0=t/l 1=t/r 2=b/l 3=b/r.
 
-;; Rectangle restored by undo.
-(defvar cua--restored-rectangle nil)
+INS specifies whether to insert on left(nil) or right(t) side.
 
-;; Last rectangle copied/killed; nil if last kill was not a rectangle.
-(defvar cua--last-killed-rectangle nil)
+If VIRT is non-nil, virtual straight edges are enabled.
 
-;; List of overlays used to display current rectangle.
-(defvar cua--rectangle-overlays nil)
+If SELECT is a regexp, only lines starting with that regexp are affected.")
+(make-variable-buffer-local 'cua--rectangle)
+
+(defvar cua--last-rectangle nil
+  "Most recent rectangle geometry.
+A CONS cell, the car of which is the rectangle's buffer, and the
+cdr of which is a cua-rectangle definition. See
+`cua--rectangle'.")
+
+
+(defvar cua--restored-rectangle nil
+  "Rectangle restored by undo.")
+
+
+(defvar cua--last-killed-rectangle nil
+  "Last rectangle copied/killed; nil if last kill was not a rectangle.")
+;; NOT TRUE:
+;; + It seems to be the two most recent killed rectangles, and is not
+;;   reset upon either a `kill-region' or `kill-line'
+;; + In the following example, the rectangle full of question marks
+;;   was killed prior to the rectangle with the string "active".
+;;      (#("???e\n??? \n???i\n???," 0 19
+;;         (yank-handler
+;;          (rectangle--insert-for-yank
+;;           ("???e" "??? " "???i" "???,")
+;;           t)))
+;;       "active " "sert on" " straig" " lines ")
+
+
+(defvar cua--rectangle-overlays nil
+  "List of overlays used to display current rectangle.")
 (make-variable-buffer-local 'cua--rectangle-overlays)
 (put 'cua--rectangle-overlays 'permanent-local t)
 
@@ -393,10 +425,23 @@
         (cua--rectangle-corner -1))
     (cua--rectangle-resized)))
 
+(defun cua-resize-rectangle-back-to-indentation ()
+  "Resize rectangle to first non-whitespace character on the left."
+  (interactive)
+  (unless (bolp)
+    (back-to-indentation)
+    (cua--rectangle-left (current-column))
+    (if (cua--rectangle-right-side)
+        (cua--rectangle-corner -1))
+    (cua--rectangle-resized)))
+
 (defun cua-resize-rectangle-bot ()
   "Resize rectangle to bottom of buffer."
   (interactive)
   (goto-char (point-max))
+  (let ((col (cua--rectangle-column)))
+    (when (< (current-column) col)
+      (cua--forward-line -1)))
   (move-to-column (cua--rectangle-column))
   (cua--rectangle-bot t)
   (cua--rectangle-resized))
@@ -483,6 +528,8 @@
   (interactive "e")
   (setq this-command last-command))
 
+;;; Operations on current rectangle
+
 (defun cua--rectangle-move (dir)
   (let ((moved t)
         (top (cua--rectangle-top))
@@ -518,11 +565,8 @@
       (cua--rectangle-set-corners)
       (cua--keep-active))))
 
-
-;;; Operations on current rectangle
-
 (defun cua--tabify-start (start end)
-  ;; Return position where auto-tabify should start (or nil if not required).
+  "Return position where auto-tabify should start (or nil if not required)."
   (save-excursion
     (save-restriction
       (widen)
@@ -538,15 +582,15 @@
 	       start)))))
 
 (defun cua--rectangle-operation (keep-clear visible undo pad tabify &optional fct post-fct)
-  ;; Call FCT for each line of region with 4 parameters:
-  ;; Region start, end, left-col, right-col
-  ;; Point is at start when FCT is called
-  ;; Call fct with (s,e) = whole lines if VISIBLE non-nil.
-  ;; Only call fct for visible lines if VISIBLE==t.
-  ;; Set undo boundary if UNDO is non-nil.
-  ;; Rectangle is padded if PAD = t or numeric and (cua--rectangle-virtual-edges)
-  ;; Perform auto-tabify after operation if TABIFY is non-nil.
-  ;; Mark is kept if keep-clear is 'keep and cleared if keep-clear is 'clear.
+  "Call FCT for each line of region with 4 parameters:
+Region start, end, left-col, right-col
+Point is at start when FCT is called
+Call fct with (s,e) = whole lines if VISIBLE non-nil.
+Only call fct for visible lines if VISIBLE==t.
+Set undo boundary if UNDO is non-nil.
+Rectangle is padded if PAD = t or numeric and (cua--rectangle-virtual-edges)
+Perform auto-tabify after operation if TABIFY is non-nil.
+Mark is kept if keep-clear is 'keep and cleared if keep-clear is 'clear."
   (let* ((inhibit-field-text-motion t)
 	 (start (cua--rectangle-top))
          (end   (cua--rectangle-bot))
@@ -683,9 +727,9 @@
     (nreverse rect)))
 
 (defun cua--insert-rectangle (rect &optional below paste-column line-count)
-  ;; Insert rectangle as insert-rectangle, but don't set mark and exit with
-  ;; point at either next to top right or below bottom left corner
-  ;; Notice: In overwrite mode, the rectangle is inserted as separate text lines.
+  "Insert rectangle as insert-rectangle, but don't set mark and exit with
+point at either next to top right or below bottom left corner
+Notice: In overwrite mode, the rectangle is inserted as separate text lines."
   (if (eq below 'auto)
       (setq below (and (bolp)
                        (or (eolp) (eobp) (= (1+ (point)) (point-max))))))
@@ -1468,6 +1512,8 @@
   (define-key cua--rectangle-keymap [remap previous-line]       'cua-resize-rectangle-up)
   (define-key cua--rectangle-keymap [remap end-of-line]         'cua-resize-rectangle-eol)
   (define-key cua--rectangle-keymap [remap beginning-of-line]   'cua-resize-rectangle-bol)
+  (define-key cua--rectangle-keymap [remap move-end-of-line]       'cua-resize-rectangle-eol)
+  (define-key cua--rectangle-keymap [remap move-beginning-of-line] 'cua-resize-rectangle-bol)
   (define-key cua--rectangle-keymap [remap end-of-buffer]       'cua-resize-rectangle-bot)
   (define-key cua--rectangle-keymap [remap beginning-of-buffer] 'cua-resize-rectangle-top)
   (define-key cua--rectangle-keymap [remap scroll-down]         'cua-resize-rectangle-page-up)
@@ -1487,7 +1533,7 @@
   (define-key cua--rectangle-keymap "\r"     'cua-rotate-rectangle)
   (define-key cua--rectangle-keymap "\t"     'cua-indent-rectangle)
 
-  (define-key cua--rectangle-keymap [(control ??)] 'cua-help-for-rectangle)
+  (define-key cua--rectangle-keymap (kbd "M-?") 'cua-help-for-rectangle)
 
   (define-key cua--rectangle-keymap [mouse-1]	   'cua-mouse-set-rectangle-mark)
   (define-key cua--rectangle-keymap [down-mouse-1] 'cua--mouse-ignore)
@@ -1512,6 +1558,9 @@
   (cua--rect-M/H-key ?k	'cua-cut-rectangle-as-text)
   (cua--rect-M/H-key ?l	'cua-downcase-rectangle)
   (cua--rect-M/H-key ?m	'cua-copy-rectangle-as-text)
+; (cua--rect-M/H-key ?m	'cua-copy-rectangle-as-text) ;; TBD
+  (define-key cua--rectangle-keymap [remap back-to-indentation] 'cua-resize-rectangle-back-to-indentation)
+
   (cua--rect-M/H-key ?n	'cua-sequence-rectangle)
   (cua--rect-M/H-key ?o	'cua-open-rectangle)
   (cua--rect-M/H-key ?p	'cua-toggle-rectangle-virtual-edges)