Library misc-cmds.el has long had this feature. These are the relevant commands, and suggested key bindings:
The first two of these are needed to remove the default remappings. (define-key visual-line-mode-map [remap move-beginning-of-line] nil) (define-key visual-line-mode-map [remap move-end-of-line] nil) (define-key visual-line-mode-map [home] 'beginning-of-line+) (define-key visual-line-mode-map [end] 'end-of-line+) (define-key visual-line-mode-map "\C-a" 'beginning-of-visual-line+) (define-key visual-line-mode-map "\C-e" 'end-of-visual-line+) Personally, I never use visual-line mode, so I just bind C-a and C-e to beginning-of-line+ and end-of-line+, respectively.
Here is what C-h f end-of-line+ says, as one example:
end-of-line+ is an interactive compiled Lisp function in `misc-cmds.el'. It is bound to C-e, end. (end-of-line+ &optional N) Move cursor to end of current line or end of next line if repeated. This is similar to `end-of-line', but: If called interactively with no prefix arg: If the previous command was also `end-of-line+', then move to the end of the next line. Else, move to the end of the current line. Otherwise, move to the end of the Nth next line (Nth previous line if N<0). Command `end-of-line', by contrast, moves to the end of the (N-1)th next line.