The command end-of-buffer
uses a variation on (goto-char (point-max))
to go to the last position in the buffer. If the buffer ends with a newline that, end-of-buffer
will leave you on the empty line at the end --, whereas if you delete thatthis last newline then, end-of-buffer
leaveswill leave you at the end of the text in the now non-empty last line.
(advice-add 'end-of-buffer :after
(lambda (&rest args) (when (looking-at-p "^$") (previous-line))))
(defun my-end-of-buffer-dwim (&rest _)
"If current line is empty, call `previous-line'."
(when (looking-at-p "^$")
(previous-line)))
(advice-add #'end-of-buffer :after #'my-end-of-buffer-dwim)
If you want end-of-buffer
to always leave you at the beginningbeginning of the lastpenultimate line of text (rather than at the end if the file has no trailing newline), you might want this instead:
(defun end-of-buffer-dwim (&rest args)
(when (looking-at-p "^$")
(previous-line))
(beginning-of-line))
(advice-add 'end-of-buffer :after 'end-of-buffer-dwim)
(defun my-end-of-buffer-dwim (&rest _)
"Go to beginning of line.
If current line is empty, go to beginning of previous one
instead."
(beginning-of-line (and (looking-at-p "^$") 0)))
(advice-add #'end-of-buffer :after #'my-end-of-buffer-dwim)
(defun my-end-of-buffer ()
(interactive)
(goto-char (point-max))
(when (looking-at-p "^$")
(previous-line))
(beginning-of-line))
(global-set-key [remap end-of-buffer] 'my-end-of-buffer)
(defun my-end-of-buffer ()
"Go to beginning of last line in buffer.
If last line is empty, go to beginning of penultimate one
instead."
(interactive)
(goto-char (point-max))
(beginning-of-line (and (looking-at-p "^$") 0)))
(global-set-key [remap end-of-buffer] #'my-end-of-buffer)
Note that all of the above solutions leave you at most one line away from the end of the buffer - they do not leave you on the last non-empty line.