I'd like to use shell-command-on-region on the region, in a way that keeps the selection, even in the case the length of the output changes, and works in regular emacs and in evil mode?

Note, I've already asked this related question which is similar, just not dealing with selection: How to use an external command to modify selected words and lines?

  • 2
    Does reactivating the mark do what you expect? E.g. hit C-x C-x – YoungFrog Aug 15 '17 at 9:26
  • @YoungFrog, no it failed for a few reasons, added answer that lists them. – ideasman42 Aug 15 '17 at 12:14
  • @ideasman42 Don't know what's your issue, but it works nicely from here with vanilla Emacs, though I don't know about evil mode. – xuchunyang Aug 15 '17 at 13:31
  • @xuchunyang - even with vanilla emacs, iirc adding more text has some issues. Though maybe under some conditions it works OK. Evil mode definitely needs special handling. – ideasman42 Aug 15 '17 at 14:27

This can be done, I managed to get it working (with help from @wasamasa), notes:

  • The selection needs to be stored and restored.
  • It's important to run (setq deactivate-mark nil) so emacs can restore the selection.
  • Special handling is needed for evil-visual-line mode,
    (evil-visual-select ...) needs to be called.

Below is a wrapper for shell-command-on-region that handles re-selection.

(defun shell-command-on-region-and-select
    (start end command
           &optional output-buffer replace
           error-buffer display-error-buffer
  "Wrapper for 'shell-command-on-region', re-selecting the output.

Useful when called with a selection, so it can be modified in-place"
  (let ((buffer-size-init (buffer-size)))
     start end command output-buffer replace
     error-buffer display-error-buffer
    (setq deactivate-mark nil)
    (setq end (+ end (- (buffer-size) buffer-size-init)))
    (set-mark start)
    (goto-char end)
    ;; needed for evil line mode
    (when (string= evil-state "visual")
      (when (eq (evil-visual-type) evil-visual-line)
        (evil-visual-select start end 'line)))))

Example call:

 (region-beginning) (region-end)
 "tr A-Za-z a-zA-Z && echo ' extra text'")
  • Note that (let (deactivate-mark) ...) is another idiom of that kind that protects the let-bound variable so that changes to it are limited to the body. – wasamasa Aug 15 '17 at 18:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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