This solution parses the diff chunks to lookup a buffer location in the diff.
By default, if the current location isn't part of the diff, the closest line in the diff is used.
(defun diff-file-line-to-point (current-filename-relative current-line current-column &optional only-exact) " Given a diff buffer, navigate to a location based on a file/location in the original file. " (save-excursion (unless (re-search-forward (concat ;; Prefix '+++ '. "^" "\\-\\-\\-[[:blank:]]+.*\n" "\\+\\+\\+[[:blank:]]+" ;; Optional 'b/'. "\\(\\|b/\\)" (regexp-quote current-filename-relative) ;; Optional ' (some text)' ;; Subversion quirk. "\\(\\|[[:blank:]]+.*\\)\n" "@@[[:blank:]]+.*[[:blank:]]@@" ;; may have trailing text, ignore this. ) nil t 1 ) (error "Diff does not contain file name %S" current-filename-relative)) (beginning-of-line) (let ( ;; next file or end of document. (point-found nil) ;; Find the next hunk or file max, to restrict the search. (diff-max (if (save-excursion (re-search-forward (concat ;; Prefix '+++ '. "^" "\\-\\-\\-[[:blank:]]+.*\n" "\\+\\+\\+[[:blank:]]+.*\n" "@@[[:blank:]]+.*[[:blank:]]@@" ;; may have trailing text, ignore this. ) nil t 1 ) (point) ) (point-max) ) ) (best-line-delta -1) (best-line nil) (best-column 0) (best-point nil) (best-hunk-begin nil) ) ;; Now search for the current hunk. ;; We have something like this: ;; @@ -1,4 +1,5 @@ (while (and (not (eq best-line-delta 0)) (re-search-forward (concat "^@@[[:blank:]]+" ;; Previous (ignore). "\\-" "\\([[:digit:]]+\\)\\,\\([[:digit:]]+\\)" "[[:blank:]]+" ;; Current (use). "\\+" "\\([[:digit:]]+\\)\\,\\([[:digit:]]+\\)" "[[:blank:]]+@@" ) diff-max t 1 ) ) (let* ( (diff-hunk-begin (string-to-number (buffer-substring-no-properties (match-beginning 3) (match-end 3)))) (diff-hunk-lines (string-to-number (buffer-substring-no-properties (match-beginning 4) (match-end 4)))) (diff-hunk-end (+ diff-hunk-begin diff-hunk-lines)) ) (cond ((< current-line diff-hunk-begin) (unless only-exact (let ((delta (- diff-hunk-begin current-line))) (when (or (eq best-line-delta -1) (< delta best-line-delta)) (setq best-line-delta delta) (setq best-line diff-hunk-begin) (setq best-column 0) (setq best-point (point)) (setq best-hunk-begin diff-hunk-begin) ) ) ) ) ((> current-line diff-hunk-end) (unless only-exact (let ((delta (- current-line diff-hunk-end))) (when (or (eq best-line-delta -1) (< delta best-line-delta)) (setq best-line-delta delta) (setq best-line (- diff-hunk-end 1)) (setq best-column 0) (setq best-point (point)) (setq best-hunk-begin diff-hunk-begin) ) ) ) ) (t (setq best-line-delta 0) (setq best-line current-line) (setq best-column current-column) (setq best-point (point)) (setq best-hunk-begin diff-hunk-begin) ) ) ) ) (when (eq best-line-delta -1) (error "Diff has no chunks")) (goto-char best-point) (let ((diff-line-current best-hunk-begin)) (forward-line) ;; Avoid eternal loop (for mal-formed diffs). (while (eq point-found nil) (let ((c (char-after (point)))) (cond ((memq c '(?\s ?+)) (when (eq diff-line-current best-line) (setq point-found (+ 1 (point) current-column)) ) (setq diff-line-current (+ 1 diff-line-current))) ((eq c ?-) nil) (t (error "Diff is malformed, unexpected character %S" c)) ) ) (forward-line) ) ) (when (eq point-found nil) (error "Diff is malformed, unable to step into hunk")) point-found ) ) ) ;; Override pop-up-windows, (defun vc-root-diff-fullscreen-and-jump-to-point () " Open a diff of the repository in the current frame. Jumping to the file, then line & column if possible. " (interactive) (let ( (pop-up-windows nil) (current-filename (buffer-file-name)) (current-line (line-number-at-pos)) (current-column (- (point) (line-beginning-position))) ) (when (vc-root-diff nil) ;; It's possible we don't have a file. (when current-filename (let* ( (current-filename-relative (file-relative-name current-filename default-directory)) (point-found (diff-file-line-to-point current-filename-relative current-line current-column)) ) ;; Go to the file in the diff which we were previously viewing. (when point-found (goto-char point-found) ) ) ) ) ) )