1

I need to prepare org files like this

* firsth things

...

  • compress files

#+begin_src shell tar cfzv {migration-date}/{client-id}from{start-date}to{finish-date}.tar.gz 1/{soc-id}/{client-id}/ #+end_src

  • more things

...

And i created this elisp function in porder to replace this strings "{strings}"

(defun fullfill-client-template (start end)
  "Adapt migration template in REGION START END to client."
  (interactive "r")
  (let ((tenant-id (read-from-minibuffer "tenant id: "))
        (soc-id (read-from-minibuffer "soc id: "))
        (client-id (read-from-minibuffer "client id: "))
        (migration-date (read-from-minibuffer "migration date: "))
        (start-date (read-from-minibuffer "start date: "))
        (finish-date (read-from-minibuffer "finish date: ")))
    (replace-string-in-region "{tenant-id}" tenant-id start end)
    (replace-string-in-region "{soc-id}" soc-id start end)
    (replace-string-in-region "{client-id}" client-id start end)
    (replace-string-in-region "{migration-date}" migration-date start end)
    (replace-string-in-region "{start-date}" start-date start end)
    (replace-string-in-region "{finish-date}" finish-date start end)
    (message (format "Migrate client: %s/%s/%s from: %s to %s at %s"
                    tenant-id soc-id client-id start-date finish-date migration-date))))

I get the following error:

Selecting only the section compress files

(error "End after end of buffer")

but when I use edebug the variables start and end are always the same:

    Result: "1"
Result: "2"

Result: "3"

Result: "4"

Result: "5"

Result: "6"

Result: "1"

Result: 23 (#o27, #x17, ?\C-w)

Result: 178 (#o262, #xb2, ?²)

Result: 1 (#o1, #x1, ?\C-a)

Result: "2"

Result: 23 (#o27, #x17, ?\C-w)

Result: 178 (#o262, #xb2, ?²)

Result: 1 (#o1, #x1, ?\C-a)

Result: "3"

Result: 23 (#o27, #x17, ?\C-w)

Result: 178 (#o262, #xb2, ?²)

Result: 2 (#o2, #x2, ?\C-b)

Result: "4"

Result: 23 (#o27, #x17, ?\C-w)

Result: 178 (#o262, #xb2, ?²)

error: "End after end of buffer"

file-truename: End after end of buffer

NickD
  • 29,717
  • 3
  • 27
  • 44
anquegi
  • 749
  • 5
  • 22
  • 1
    I get no such error: the template variables are replaces by whatever reply I enter at each prompt. – NickD Oct 13 '22 at 10:19
  • I fixed the source block to say shell, not elisp, but assuming you are not actually trying to execute the source block, that should not make any difference: running the function on the region should just replace the strings - and IME it does. – NickD Oct 13 '22 at 10:23
  • Thanks for the correction, that clarifies the sample, I'm using doom emacs, I will try vith vanilla emacs – anquegi Oct 13 '22 at 12:49
  • I tried with vanilla emacs and get same error: replace-string-in-region: End after end of buffer – anquegi Oct 13 '22 at 12:56
  • Does your marked region include the end of the buffer before calling fullfill-client-template? – kozina-adjacent Oct 13 '22 at 13:30
  • no I only mark the sectionfrom 23 to 178 and works for the firsts subtitution but after that doen't work – anquegi Oct 13 '22 at 14:39

1 Answers1

1

I think I see the problem. As you are making substitutions, the end of the region (178) does not correspond to a fixed place in the buffer, it moves about depending on the length of the replacement. As long as the buffer end is at point 178 or greater, the replacement succeeds. At some point, the buffer end becomes less than 178 and the replacement barfs.

A workaround is to add a bunch of text at the end of the buffer, so that never happens (which is why I didn't see it in my first attempt).

[This solution is wrong (although it would "work" in the case above, but it is not general enough) - see the EDIT for a correct solution, as suggested in the comment] A better solution is to recalculate the effective end of the region for every replacement to be the lesser of the two values end and the position at the end of the buffer. If the end of the buffer ever becomes less than end, we will only attempt to do the replacement within the restricted region:

  (defun fullfill-client-template (start end)
    "Adapt migration template in REGION START END to client."
    (interactive "r")
    (let ((tenant-id (read-from-minibuffer "tenant id: "))
          (soc-id (read-from-minibuffer "soc id: "))
          (client-id (read-from-minibuffer "client id: "))
          (migration-date (read-from-minibuffer "migration date: "))
          (start-date (read-from-minibuffer "start date: "))
          (finish-date (read-from-minibuffer "finish date: ")))
      (replace-string-in-region "{tenant-id}" tenant-id start (min end (buffer-end 1)))
      (replace-string-in-region "{soc-id}" soc-id start (min end (buffer-end 1)))
      (replace-string-in-region "{client-id}" client-id start (min end (buffer-end 1)))
      (replace-string-in-region "{migration-date}" migration-date start (min end (buffer-end 1)))
      (replace-string-in-region "{start-date}" start-date start (min end (buffer-end 1)))
      (replace-string-in-region "{finish-date}" finish-date start (min end (buffer-end 1)))
  (message (format "Migrate client: %s/%s/%s from: %s to %s at %s"
                  tenant-id soc-id client-id start-date finish-date migration-date))))

An even better way perhaps is to use a marker instead of an absolute position, but I have not gone down that path yet.

EDIT: As a comment points out, the above solution is incorrect. A marker solution could probably be made to work, but as the comment also points out, there is a better solution:

  (defun fullfill-client-template (start end)
    "Adapt migration template in REGION START END to client."
    (interactive "r")
    (let ((tenant-id (read-from-minibuffer "tenant id: "))
          (soc-id (read-from-minibuffer "soc id: "))
          (client-id (read-from-minibuffer "client id: "))
          (migration-date (read-from-minibuffer "migration date: "))
          (start-date (read-from-minibuffer "start date: "))
          (finish-date (read-from-minibuffer "finish date: ")))
      (save-restriction
        (narrow-to-region start end)
        (replace-string-in-region "{tenant-id}" tenant-id (point-min) (point-max))
        (replace-string-in-region "{soc-id}" soc-id (point-min) (point-max))
        (replace-string-in-region "{client-id}" client-id (point-min) (point-max))
        (replace-string-in-region "{migration-date}" migration-date (point-min) (point-max))
        (replace-string-in-region "{start-date}" start-date (point-min) (point-max))
        (replace-string-in-region "{finish-date}" finish-date (point-min) (point-max)))
  (message (format "Migrate client: %s/%s/%s from: %s to %s at %s"
                  tenant-id soc-id client-id start-date finish-date migration-date))))

Thanks to @Gilles 'SO- stop being evil' for the solution.

NickD
  • 29,717
  • 3
  • 27
  • 44
  • 1
    Correct diagnosis but incorrect solution: the end position changes with each replacement, (min end (buffer-end 1)) may be too much or too little at any point depending on the lengths of the strings involved. Rather than use markers, it's easier to use (narrow-to-region start end) at the beginning and then do all the replacements from (point-min) to (point-max), with a save-restriction around the whole thing. – Gilles 'SO- stop being evil' Oct 13 '22 at 15:50
  • Thanks NickD and Gilles'SO-stopbeingevil' this really works, I also was trying the solution suggested by Gilles, Thanks for the analisis and for the well explained and working solution – anquegi Oct 13 '22 at 16:25