6

Q: How do I find the visual line number of point (so that I can restore it after an operation)?

By “visual line number”, I'm referring to the number N of lines between the top of the window (or the screen) and the cursor, not the top of the buffer.

At first I thought I could calculate N with the following snippet:

(- (line-number-at (point))
   (line-number-at (window-start)))

However, to complicate things, I have sections of the buffer which are invisible. So the snippet above usually returns values much larger than N.


Context:
I need to perform an operation that essentially erases the entire buffer and writes it again, but I want it to be mostly invisible to the user.

Since the buffer contents are created anew, save-excursion doesn't help in this situation. Still, I manage to preserve point position by saving it as a number, instead of a marker.

(let ((point (point)))
  (recreate-buffer)
  (goto-char point))

Q (alternative wording): Is there a similar method I can use to preserve the visual height of the cursor?
By that, I mean that if the cursor is initially on the Nth visible line of the screen, the window should be scrolled after the operation so that this remains true.

6
  • How about using the function (set-window-start (selected-window) my-stored-window-start-pos) and use the same point that you previously stored -- i.e., (let ((my-stored-window-start-pos (window-start))) . . .)
    – lawlist
    Nov 25, 2014 at 23:49
  • @lawlist That's a valid answer, but it won't quite work at the moment because the invisible sections are no longer invisible after the rebuild. I probably will do that at some point, but it will take a lot more work so I was looking for a quick way of preserving height for now.
    – Malabarba
    Nov 25, 2014 at 23:55
  • How about forward-line with test to see if its visible, and skip without counting if its invisible -- Goto predetermined window-start and then foward-line x number of visible lines. X number of visible lines is equal to your proposed test above before erasing the buffer.
    – lawlist
    Nov 26, 2014 at 1:36
  • @lawlist sounds like it would work. But it'd have to be next-line, IIRC forward-line would count invisible lines.
    – Malabarba
    Nov 26, 2014 at 1:38
  • I was thinking of something like using your calculation (e.g., N) and set-window-start at previously recorded position. Then goto that window start and commence: (let ((count 0)) (catch 'done (while t (forward-line 1) (unless (invisible-p (point-at-bol)) (setq count (1+ count))) (when (= count N) (throw 'done nil)))))
    – lawlist
    Nov 26, 2014 at 1:47

1 Answer 1

5

You can use (count-screen-lines &optional beg end count-final-newline window) (manual page) to find the number of lines shown between two points.

You can do the following to re-create the point's position on the screen:

(let ((lines-down (count-screen-lines nil (point) t))
      (lines-from-top-of-window (count-screen-lines (window-start) (point) t))
      (diff-beg-of-line (- (point) (line-beginning-position))))
  ;; Code that redraws screen goes here:
  ;; <code></code>
  ;; Now, to find your old position:
  (if (equal lines-down 0)
      ;; When the point is 1, lines-down is 0. In all other cases,
      ;; `count-screen-lines' is 1-indexed by the line. Set
      ;; `lines-down' to 1 to compensate for this edge case.
      (setq lines-down 1))
  (move-to-window-line (- lines-down 1))
  (recenter lines-from-top-of-window)
  (goto-char (+ (point) diff-beg-of-line)))
3
  • Thanks, count-screen-lines is exactly what I needed. But the implementation can be a lot simpler: (let ((point (point)) (line (count-screen-lines (window-start) (point)))) (perform-operation) (goto-char point) (recenter line))
    – Malabarba
    Nov 26, 2014 at 12:13
  • Ah, does (point) not count invisible characters? If that's the case, I'll update my example.
    – nosefrog
    Nov 26, 2014 at 15:53
  • point just counts everything (it's the logical coordinate, not the visible one), but that's not a problem. I want point to be exactly where it was, and then recenter the window so that the line in which point is located represents the same screen line which it was before.
    – Malabarba
    Nov 26, 2014 at 16: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.