3
votes

I am trying to find a package/function in emacs where it would have both files opened side by side to be at same line location mirroring the movement of whichever buffer is moving. Meaning, for two buffers opened side by side, moving in one of the buffers (page up/down, moving cursor.,..etc) would have the same movements in the other buffer.

More specifically, when opening a buffer (and while activating this mode) the opened buffer should already be at the line position of the one that is already opened in the other buffer window.

1
No. This is something completely different. I usually open the mercurial ediff of a specific file in the second buffer, it includes the developer name and date when he last changed each source line. the original buffer will be colored and the mercurial one won;t because it starts with the names and treated as a text file. I want to have both files navigate at the same lines when moving between them so I can connect the "colored" source line from the original buffer to the "uncolored" mercurial one in the next window. Not sure if that clears it up or added more confusion. - SFbay007
Thank you Ben, that is actually very close. But since when opening both buffers the lines has to be alined, your suggestion won't be a complete fix. Because I could be at the bottom of the buffer and when doing "C-c l" the second buffer will align in the middle of the screen and so I won't have both lines on the same horizontal level. I will also want the second buffer to mirror the movements in the first buffer, page up/down, cursor moves...everything. - SFbay007

1 Answers

5
votes

You could try scroll-all-mode. This switches on parallel scrolling of all windows of one frame.

Scrolling with the mouse and scroll bar do not work for me. But all scrolling with keys (such as Pg-Down, Pg-Down and cursor movements) works fine.

EDIT: You could also try the following code. It works only for a frame with exactly two windows not counting the minibuffer. You have to open the files at first and make sure that they are shown in two windows side-by-side. Then define the master window by activating sync-window-mode for it. Make sure that line-wrapping is off for both windows.

EDIT: Fixed a problem (sometimes one had to press buttons twice to sync). Solution: Also hook to window-scroll-functions. At post-command-hook the right window-start position is not known since redisplay did not run yet. The fist point where the future window-start is known is at window-scroll-functions.

(defun sync-window (&optional display-start)
  "Synchronize point position other window in current frame.
Only works if there are exactly two windows in the active wrame not counting the minibuffer."
  (interactive)
  (when (= (count-windows 'noMiniBuf) 2)
    (let ((p (point))
      (start (or display-start (window-start)))
      (vscroll (window-vscroll)))
      (other-window 1)
      (goto-char (min (max p (point-min)) (point-max)))
      (set-window-start (selected-window) start)
      (set-window-vscroll (selected-window) vscroll)
      (other-window 1)
      )))

(define-minor-mode sync-window-mode
  "Synchronized view of two buffers in two side-by-side windows."
  :group 'windows
  :lighter " ⇕"
  (if sync-window-mode
      (progn
    (add-hook 'post-command-hook 'sync-window-wrapper 'append t)
    (add-to-list 'window-scroll-functions 'sync-window-wrapper)
    (sync-window))
    (remove-hook 'post-command-hook 'sync-window-wrapper t)
    (setq window-scroll-functions (remove 'sync-window-wrapper window-scroll-functions))
    ))

(defun sync-window-wrapper (&optional window display-start)
  "This wrapper makes sure that `sync-window' is fired from `post-command-hook'
only when the buffer of the active window is in `sync-window-mode'."
  (with-selected-window (or window (selected-window))
    (when sync-window-mode
      (sync-window display-start))))

(provide 'sync-window)

Next, a version that synchronizes lines instead of character positions.

If you select some lines in the master buffer this also marks the corresponding lines in the slave buffer. The highlighting keeps permanent until you select another region in the master buffer. It even survives deactivation of sync-window-mode in the master buffer. You get ride of the highlighting by

M-x sync-window-cleanup

in the slave buffer.

(defface sync-window-face ;; originally copied from font-lock-function-name-face
  '((((class color) (min-colors 88) (background light)) (:foreground "Yellow" :background "Blue1"))
    (((class color) (min-colors 88) (background dark)) (:foreground "Red" :background  "LightSkyBlue"))
    (((class color) (min-colors 16) (background light)) (:foreground "Blue" :background "Yellow"))
    (((class color) (min-colors 16) (background dark)) (:foreground "LightSkyBlue" :background "Yellow"))
    (((class color) (min-colors 8)) (:foreground "blue" :bold t))
    (t (:bold t)))
  "Face used to highlight regions in `sync-window-mode' slaves."
  :group 'sync-window)

(defvar sync-window-overlay nil
  "Overlay for current master region in `sync-window-mode' slaves.")
(make-variable-buffer-local 'sync-window-overlay)

(defun sync-window-cleanup ()
  "Clean up after `sync-window-mode'."
  (interactive)
  (if (overlayp sync-window-overlay)
      (progn
    (delete-overlay sync-window-overlay)
    (setq sync-window-overlay nil))
    (remove-overlays (point-min) (point-max) 'sync-window-slave t)))

(defvar sync-window-master-hook nil
  "Hooks to be run by `sync-window' in the master window ")

(defun sync-window (&optional display-start)
  "Synchronize point position other window in current frame.
Only works if there are exactly two windows in the active wrame not counting the minibuffer."
  (interactive)
  (when (= (count-windows 'noMiniBuf) 2)
    (let ((p (line-number-at-pos))
      (start (line-number-at-pos (or display-start (window-start))))
      (vscroll (window-vscroll))
      breg ereg)
      (when (use-region-p)
    (setq breg (line-number-at-pos (region-beginning))
          ereg  (line-number-at-pos (if (looking-back "\n") (1- (region-end)) (region-end)))))
      (run-hooks 'sync-window-master-hook)
      (other-window 1)
      (goto-char (point-min))
      (when breg
    (sync-window-cleanup)
    (overlay-put (setq sync-window-overlay (make-overlay (line-beginning-position breg) (line-end-position ereg))) 'face 'sync-window-face)
    (overlay-put sync-window-overlay 'sync-window-slave t))
      (setq start (line-beginning-position start))
      (forward-line (1- p))
      (set-window-start (selected-window) start)
      (set-window-vscroll (selected-window) vscroll)
      (other-window 1)
      (unless display-start
    (redisplay t))
      )))

(defvar sync-window-mode-hook nil
  "Hooks to be run at start of `sync-window-mode'.")

(define-minor-mode sync-window-mode
  "Synchronized view of two buffers in two side-by-side windows."
  :group 'windows
  :lighter " ⇕"
  (if sync-window-mode
      (progn
    (add-hook 'post-command-hook 'sync-window-wrapper 'append t)
    (add-to-list 'window-scroll-functions 'sync-window-wrapper)
    (run-hooks 'sync-window-mode-hook)
    (sync-window))
    (remove-hook 'post-command-hook 'sync-window-wrapper t)
    (setq window-scroll-functions (remove 'sync-window-wrapper window-scroll-functions))
    ))

(defun sync-window-wrapper (&optional window display-start)
  "This wrapper makes sure that `sync-window' is fired from `post-command-hook'
only when the buffer of the active window is in `sync-window-mode'."
  (with-selected-window (or window (selected-window))
    (when sync-window-mode
      (sync-window display-start))))

(provide 'sync-window)