0

Context: I want to create a command that can increment a number or character contained within an arbitrary area of a buffer. I've reduced the problem to calling some function to set the text of a narrowed buffer, a la replace-region-contents, with the following additional properties:

  1. markers that start on either end of the region (including mark and point themselves) stay on either end of the replaced text
  2. markers that start within the region are bounded by the region (they stay within the region or at either end)
  3. the positions taken as parameters to the command are not treated specially, and the command does not explicitly set point or mark
  4. the command does not print any messages

No method I know of so far has all the above properties.

  • The best by far is replace-region-contents itself, and when the original and resulting strings are similar, it is perfect. But otherwise, the first property is not upheld
  • replace-regexp seems to be appropriate except that it prints messages on every operation
  • manually deleting and inserting does not respect markers at all
  • I thought maybe manually inserting then deleting would be better, but it is not

Is there any function that does what I want? If not, how would I do this myself?

Edit: Prompted by @phils, I tried to reproduce the trouble I was having earlier with replace-region-contents

As requested, here is a recipe / test showing that markers are not preserved by replace-region-contents in the way I'd like for this case:

(progn
  'original ; < this symbol will be replaced
  (setq mark1 (copy-marker 10))
  (setq mark2 (copy-marker 19))
  (setq replacement "'replacement") ; < with this

  (replace-region-contents mark1 mark2 (lambda () replacement))
  (set-mark mark1)
  (goto-char mark2)
  (setq deactivate-mark nil)
  ;; and I want for markers that surrounded the text before replacement
  ;; to surround the new text after replacement
  (cl-assert (eq (marker-position mark1) 10) :show)
  (cl-assert (eq (marker-position mark2) (+ mark1 (length replacement))) :show)
  )

second edit: my test was flawed, and my description of the constraints were inaccurate. I have now updated both the conditions and the test.

Having typed all this out, though, I suspect that a "solution" could end up being a bit of a hack, and my approach may be over-engineered. I might just reduce the problem to working with only mark and point, not caring about where other markers end up.

2
  • Can you provide a recipe to reproduce "markers that start on either end of the region stay on either end" failing with replace-region-contents? Markers outside the region (and I'm assuming that "just outside the region" is what you mean) ought to retain their relative positions. If they don't, please M-x report-emacs-bug. Markers inside the region are subject to the text changes.
    – phils
    Commented Oct 2 at 5:05
  • If you actually meant "just inside the region" then maybe you should simply add a new pair of markers outside it beforehand, in order to reliably (AFAIK) track those positions.
    – phils
    Commented Oct 2 at 5:09

1 Answer 1

0

It looks like the function replace-regexp-in-region does what I need. it isn't printing messages, but it is otherwise behaving like replace-regexp. It doesn't seem ideal to arbitrarily use regexps for this, but it works.

(replace-regexp-in-region (rx string-start (zero-or-more anychar) string-end) str start end)

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.