0

I have some code that loads a file into a temp buffer and tries to do some sub-string operations on the temp buffer. But I have noticed that the same code and file sometimes gives different results and cannot figure out what I'm doing wrong.

I have simplified the code to create a minimal example:

file test.txt:

aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa 

so just 10 lines of 'a'.

Then I have some code that goes to a given position, the beginning of the line and then moves one line up with line-move-1 (using line-move shows the same effect):

(defun move-around (pos) (goto-char pos) (goto-char (line-beginning-position)) (message "above: pos %s point %s" pos (point)) (line-move-1 -1) (message "point now is %s" (point))) 

And the code that loads the file and calls the function:

(defun test-move () (with-temp-buffer (insert-file-contents-literally "./test.txt" nil nil nil t) (move-around 108))) 

When I call this with:

(test-move) 

I expected the code to deterministically always return the same result. However, most of the time, the result is "point now is 89" (which is the result I expect), but sometimes the result is "point now is 91" or "point now is 93"

Example Output in Messages:

above: pos 108 point 100 point now is 89 "point now is 89" above: pos 108 point 100 point now is 93 "point now is 93" above: pos 108 point 100 point now is 93 "point now is 93" above: pos 108 point 100 point now is 93 "point now is 93" above: pos 108 point 100 point now is 89 "point now is 89" above: pos 108 point 100 point now is 89 "point now is 89" above: pos 108 point 100 point now is 89 "point now is 89" above: pos 108 point 100 point now is 89 "point now is 89" 

What could be the cause for the same code giving different results?

The point before the call to line-move-1 is at the first character of the last line, so I don't see how it could result in point being 91 or 93 afterwards, which is in the middle of the second to last line, rather than the beginning of that line.

I can only reproduce this when I run the code multiple times by using eval-last-sexp, and it seems to matter if I move the cursor around in my code buffer in between invocations of eval-last-sexp. When I do

 (defun call-often () (dotimes (i 100) (test-move))) 

all 100 times I get the same "89" result.

Any advice on how I can debug further what exactly is happening here? What can influence movements within with-temp-buffer while the code is running?

Edit: I can reproduce this not only on my main machine, but also on my Phone running emacs in Android Termux and my Notebook.

I think the best way I can reproduce it is by doing this:

  • put the functions and the invocation into a file so the file looks like:

     (defun move-around (pos) (goto-char pos) (goto-char (pos-bol)) (message "above: pos %s point %s, buffer-size %s" pos (point) (buffer-size)) (line-move-1 -1) (message "point now is %s, buffer-size %s" (point) (buffer-size))) (defun test-move () (with-temp-buffer (insert-file-contents-literally "./test.txt" nil nil nil t) (move-around 108))) (test-move) 
  • eval-last-sexp the functions and then the (test-move) It returns 89 as expected

  • add a two new lines below, insert three space characters, go a line back up (so cursor is back at the line just below (test-move))

  • eval-last-sexp now returns 91 quite reliably for me

  • It seems like the exact result depends on the number of spaces I entered between invocations.

7
  • I can not reporiduce it here using eval-last-sexp. But you can try printing the (buffer-size) additionally... see if that is constant. Commented Feb 18, 2024 at 21:17
  • Thanks for trying it @dalanicolai! The buffer-size stays constant at 110 in both cases when point is 89 and 91 / 93. I'll try if I can reproduce it in my work machine tomorrow. Commented Feb 18, 2024 at 21:46
  • I can reproduce it in two other machines and updated the exact steps I'm doing to reproduce it. Commented Feb 18, 2024 at 22:24
  • 1
    Comment out the calls to message, and just do M-x debug-on-entry RET test-move RET. Then walk through the debugger with d (or c to skip through a step). That should show you exactly what's going on. Commented Feb 19, 2024 at 0:59
  • 2
    Okay, I can reproduce it now indeed. Weird indeed. As the comment before the line-move-1 definition says it is the gut of previous-line, I have tried using previous-line also, then the behavior is even more weird. Also, I have tried (forward -1) in which case the function seems to work correctly. Furthermore, I have used all variants of move-around within test.txt itself, in which case all variants seem to work fine. I have no idea what happens here (you could file a bug and you might get an answer), but at least using (forward-line -1) seems to work correctly. Commented Feb 19, 2024 at 8:44

1 Answer 1

2

Thanks @Dalanicolai and @drew, I think the answer is:

  • don't use move-line-1 but use (forward-line -1) instead

What seems to be happening is that line-move-1 calls line-move-finish (https://github.com/emacs-mirror/emacs/blob/master/lisp/simple.el#L8124) with goal-column derived from variable temporary-goal-column. (https://github.com/emacs-mirror/emacs/blob/master/lisp/simple.el#L8005)

I can see that doing the movements before calling eval-last-sexp is affecting the value of temporary-goal-column, and line-move-1 then moves to the goal-column.

In summary, I think move-line-1 is simply not meant to be used in scripts like this.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.