I often find myself swapping pieces of text.

How can I make a command to replace the current text with the latest kill, then put the text I just replaced at the position of the previous kill?

Or is there a better way to achieve the same effect?

  • Maybe there's a better approach, but I use combinations of set-mark-command, yank-pop, kill-ring-save, and transpose-* commands (f.i. transpose-paragraphs).
    – Y. E.
    May 20, 2023 at 8:53

1 Answer 1


Maybe what you are looking for is something like this?

(setq my-positions-of-previous-kill nil) ;; keep previous kill position

(defun my-kill-region ()
  "Save position before kill-region."
  (let* ((beg (mark))
         (end (point)))
    (unless (and beg end)
      (user-error "The mark is not set now, so there is no region"))
    (setq my-positions-of-previous-kill (list beg)) ;; save kill position
    (kill-region beg end)))

(defun my-position (&optional from-text)
  "Only if from-text and the symbol under cursor matched, returns the position of region or symbol under cursor and the text."
  (when-let* ((bounds
               (if (use-region-p)
                   (cons (region-beginning) (region-end))
                 (bounds-of-thing-at-point 'symbol)))
              (beg (car bounds))
              (end (cdr bounds))
              (current-text (buffer-substring-no-properties beg end))
              (from-text (or from-text current-text)))
    (when (and bounds (string= current-text from-text))
      (list (list beg end) from-text))))

(defun my-swap-current-text ()
  (when-let ((result (my-position))) ;; current position and text
    (let* ((beg (car (car result))) ;; beginning of region or symbol under cursor
           (from-text (car (last result))) ;; current-text as from-text to replace
           (positions (sort (append my-positions-of-previous-kill (list beg)) '<)) ;; sort positions including current cursor position
           (new-positions ()) ;; to store positions after adding offsets
           (to-text (car kill-ring)) ;; text to yank
           (offset (- (length to-text) (length from-text))) ;;  new word length - previous one
           (increment 0))
      (while positions ;; replace other positions
        (let* ((position (car positions))
               (new-position (+ position increment))) ;; adding offsets
          (goto-char new-position) ;; go to a position of previous kill
          (when-let* ((other-result (my-position from-text)) ;; getting bound of each previous position
                      (other-beg (car (car other-result)))
                      (other-end (car (last (car other-result)))))
            (delete-region other-beg other-end) ;; delete and
            (insert to-text)                    ;; insert
            (setq new-positions (append new-positions (list new-position))) ;; stacking positions to update my-positions-of-previous-kill later
            (setq increment (+ increment offset))) ;; cumulative offsets
          (setq positions (cdr positions)))) ;; for loop
      (setq my-positions-of-previous-kill new-positions) ;; replace with new-positions after offset
      (kill-new from-text)))) ;; push

(define-key global-map [remap kill-region] 'my-kill-region) ;; use my-kill-region to save position before kill-region

usage: enter image description here

  • Thank you. From what I understand, that code puts the text we just replaced in the kill ring, but it doesn't then go to the position of the prior kill and insert it there?
    – Zaz
    May 20, 2023 at 13:14
  • I misunderstood both ("the latest kill" and "the previous kill") were in kill-ring. I think the code would be quite complex if it is expected to replace not only the text under cursor, but also the text under the previous killing position because it is necessary to always store the two positions and then re-calculate the other position after replacing one of them. Perhaps it would be easier to create a function using (replace-string) for entire buffer (or region) rather than the idea of swapping.
    – roomworoof
    May 21, 2023 at 5:24
  • posted the function using (replace-string)
    – roomworoof
    May 21, 2023 at 6:46
  • revised the code but it appears to be a much more complex feature than I imagined, or I made the specification unnecessarily complicated.
    – roomworoof
    May 21, 2023 at 11:00

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.