0

In a few functions I've written logic that does the following:

(defun replace-in-region (str beg end)
  "Replace the region between BEG & END with STR."
  (save-excursion 
    (goto-char beg)
    (delete-region beg end)
    (insert str)))

There is some opportunity avoid redundant work (and redundant storage in the undo-stack), by contracting the region for parts of the buffer that already match the beginning/end of the string.

There is replace-buffer-contents which is useful for replacing buffer contents but as far as I can tell it can't be limited to a region.

Is there a utility to do this? Or do I need to write my own?

2
  • 2
    The doc string of replace-buffer-contents says: Replace accessible portion of current buffer with that of SOURCE.` So you can limit the accessible portion of the buffer to the region with narrow-to-region. Untested, but I don't see any reason why it would not work.
    – NickD
    Commented Oct 16, 2022 at 3:37
  • 1
    Saw this but in my initial tests it seemed replace-buffer-content ignored narrowing. It turn out my test was in a before-save-hook which I suspect is widening the buffer, so I think this could be made into an answer.
    – ideasman42
    Commented Oct 16, 2022 at 3:44

1 Answer 1

0

Answering my own quiestion with a utility funciton that calls delete-region and insert with additional checks for matching beginning & end.

(defun my-buffer-replace-region (str beg end)
  "Replace region from BEG to END with STR."
  (let ((len (length str))
        (i-beg nil)
        (i-end nil)
        (i-end-ofs nil))

    ;; Check for skip end.
    (let ((i 0))
      (let ((len-test (min (- end beg) len)))
        (while (< i len-test)
          (let ((i-next (1+ i)))
            (cond
             ((eq (aref str (- len i-next)) (char-after (- end i-next)))
              (setq i i-next))
             (t ;; Break.
              (setq len-test i))))))
      (unless (zerop i)
        (setq i-end (- len i))
        (setq len (- len i))
        (setq end (- end i))
        (setq i-end-ofs i)))

    ;; Check for skip start.
    (let ((i 0))
      (let ((len-test (min (- end beg) len)))
        (while (< i len-test)
          (cond
           ((eq (aref str i) (char-after (+ beg i)))
            (setq i (1+ i)))
           (t ;; Break.
            (setq len-test i)))))
      (unless (zerop i)
        (setq i-beg i)
        (setq beg (+ beg i))))

    (when (or i-beg i-end)
      (setq str (substring str (or i-beg 0) (or i-end len))))

    (goto-char beg)
    (delete-region beg end)
    (insert str)
    (when i-end-ofs
      ;; Leave the cursor where it would be if the end wasn't clipped.
      (goto-char (+ (point) i-end-ofs)))
    (cons beg (+ beg (length str)))))

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.