4

A common use-case that I have is to add a string to the same position of each line of text.

For example, I might start with

expect(one).toEqual(true);
expect(two).toEqual(false);

which I want to transform into

expect(oneValue).toEqual(true);
expect(twoValue).toEqual(false);

What I currently do is:

  • place my cursor after one
  • press C-x <SPC> (invoking rectangle-mark-mode)
  • press C-n to extend the zero-width rectangle down one row
  • (look up the keystrokes and then) press C-x r t Value <RET> to invoke string-rectangle, thereby adding the string Value to the desired locations

I would like to simply start typing after selecting a rectangle with rectangle-mark-mode, and the rectangular selection would be interactively replaced with what I'm typing (including the case where I've marked a zero-width rectangle). In other words, I want to eliminate the C-x r t step above and just start typing Value immediately. How can I accomplish this?

I'm also curious why this isn't the default behavior of Emacs, since I can't think of a disadvantage to this approach.

6
  • There is no magic. Keys are all equals in Emacs. C-x r t runs a command, V, a, l, u and e all run a command as well. You have to teach Emacs what's the mean of "start typing".
    – xuchunyang
    Commented Mar 14, 2018 at 10:27
  • Also multiple-cursors offers a convenient way to do this, once you get used to the key-bindings.
    – InHarmsWay
    Commented Apr 14, 2018 at 11:23
  • string-rectangle long pre-dates rectangle-mark-mode -- you can save yourself a keystroke by using C-SPC rather than C-x SPC.
    – phils
    Commented Jul 12, 2018 at 9:57
  • 1
    I recently wrote some code for this on reddit.
    – Omar
    Commented Jul 12, 2018 at 18:00
  • @Omar, would you please copy your solution into an Answer here?
    – phils
    Commented Jul 13, 2018 at 9:07

4 Answers 4

4

This came up on reddit where I wrote two ways of achieving this, but I think the nicest solution was offered by Stefan Monnier:

(defun string-rectangle-with-initial (char)
  (interactive (list last-input-event))
  (push char unread-command-events)
  (call-interactively 'string-rectangle))

(define-key rectangle-mark-mode-map
  [remap self-insert-command] 'string-rectangle-with-initial)

On Emacs 25 and later, string-rectangle has a lovely dynamic preview of the result. If you want similar interactivity on older Emacsen, you can try one of my solutions:

(defun string-rectangle-single-char ()
  (interactive)
  (when (< (point) (mark)) (exchange-point-and-mark))
  (string-rectangle (region-beginning)
                    (region-end)
                    (this-command-keys))
  (exchange-point-and-mark)
  (forward-char)
  (exchange-point-and-mark)
  (setq deactivate-mark nil))

(define-key rectangle-mark-mode-map
  [remap self-insert-command] 'string-rectangle-single-char)
3

Offhand I'm inclined to agree with the feature request -- with rectangle-mark-mode enabled, self-insert-command seems (AFAICS) to exit the rectangle mode and insert at point, just as if the rectangle wasn't selected; so your idea seems like a good idea for a new option. M-x report-emacs-bug and suggest it?

There is a shorter key sequence than the one you're using in rectangle-mark-mode-map however. If you mark the rectangle with C-x SPC then:

  • C-t runs string-rectangle.
  • C-o runs open-rectangle.

FYI I use string-rectangle that way when I want to replace existing text; but if I'm purely inserting then I tend to make use of the rectangle support in cua-selection-mode which can operate rather like what you're asking for:

  • C-RET to mark the top of the column
  • Move point down to the bottom position
  • Start typing
  • C-RET to exit

That's just one use-case for it. M-x find-library RET cua-base RET and read about "CUA rectangle support" for the full details.

1

I’ve been using Stefan Monnier’s function for some time and along the way I’ve fixed some of its shortcomings, namely, that it doesn’t trigger on yanking or when using quoted-insert, and that it duplicates the first character when executed as part of a keyboard macro.

Making it work with yank and quoted-insert is just a matter of remapping those two commands in addition to self-insert-command, and the last issue is solved by avoiding to push char to unread-command-events while executing a keyboard macro.

(defun string-rectangle-with-initial (char)
  "Call ‘string-rectangle’ before processing CHAR."
  (interactive (list last-input-event))
  (barf-if-buffer-read-only) ; Emulate ‘(interactive "*")’.
  (unless executing-kbd-macro
    (push char unread-command-events))
  (call-interactively #'string-rectangle))

(with-eval-after-load 'rect
  (define-key rectangle-mark-mode-map [remap self-insert-command] #'string-rectangle-with-initial)
  (define-key rectangle-mark-mode-map [remap yank] #'string-rectangle-with-initial)
  (define-key rectangle-mark-mode-map [remap quoted-insert] #'string-rectangle-with-initial))
-1

(This answer doesn't tell you how to do what you request. It tries to tell you why that behavior is not the default behavior.)

There are lots of things you might want to do with a rectangular region, just like there are lots of things you might want to do with a regular region. Why suppose that whatever you type should be considered only as text to be used as string-rectangle? That would seriously limit what you can do with a rectangular region.

The problem, I think, is that you are in the habit of creating a rectangular region, and not in the habit of just putting point and mark at the corners of what you want to consider as a rectangle.

(That said, maybe you could get the behavior you request, if you really want it, by fiddling with the delete-selection code.)

2
  • Good point about the analogy to regular regions. But to follow that analogy, my assumption was that anything special you'd want to do to with region would involve a modifier key such as C- or M-, and anything else would be considered an attempt to overwrite the region with text input.
    – mark
    Commented Mar 14, 2018 at 1:33
  • Thank you for the point and mark suggestion, that is an good approach. 1) Set point and mark at the corners of a rectangle 2) invoke C-x r t and begin typing
    – mark
    Commented Mar 14, 2018 at 1:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.