There are three limits that you might want to use.
- The plot corners (green points);
- The last breaks (red points);
- The actual xlim's and ylim's (blue points).

"1." Can be achieved with Allan's annotation_custom
answer. For "2." and "3." we must extract the limits and breaks. This can be done by acessing the plot metadata with ggplot_build()
. I created custom functions that save that metadata in a geom_segment
friendly format:
get_lims <- function(graph){
ggplot_build(graph)$layout$panel_params[[1]][c('x.range', 'y.range')] %>%
flatten() %>%
set_names(c('x', 'xend', 'y', 'yend'))
}
The limits/ranges are on these ...$x.range
and ...$y.range
vectors, we are simply flattening them, and saving with the geom_segment's arguments names.
get_breaks <- function(graph){
ggplot_build(graph)$layout$panel_params[[1]][c('x.sec', 'y.sec')] %>%
map(~ c(.x$breaks, .x$minor_breaks) %>% na.omit() %>% {c(min(.), max(.))}) %>%
flatten() %>%
set_names(c('x', 'xend', 'y', 'yend'))
}
Here, the breaks info are under ...$x.sec$breaks
and ...$x.sec$minor_breaks
vectors (for major and minor breaks). Thus, the map
here is joining major and minor (as any one of them could be the first/last), omitting NA's, and getting the minimal and maximal values. We then flatten and rename.
Lastly, you might be interested in expanding the red line of your example, which can be made by manually calculating a abline for it (line in purple).
Now to compare the methods:
g <- ggplot(iris, aes(x=Petal.Length, y=Petal.Width))+
geom_point() +
geom_smooth(method = "lm", se = F, color = 'black')
breaks <- get_breaks(g)
b <- (breaks$yend - breaks$y)/(breaks$xend - breaks$x)
a <- breaks$y - b*breaks$x
g +
annotation_custom(grid::linesGrob(gp = grid::gpar(col = 'green'))) +
geom_abline(slope = b, intercept = a, color = 'purple') +
geom_segment(do.call(aes, get_breaks(g)), color = 'red') +
geom_segment(do.call(aes, get_lims(g)), color = 'blue')

Obs: the points were added later.