1#' Split a string at linebreaks.
2#'
3#' Return a list of the lines in the string, breaking at line boundaries.
4#'
5#' Line breaks are not included in the resulting list unless \code{keepends} is
6#' \code{TRUE}. Line breaks include \code{"\n"}, \code{"\r"}, and \code{"\r\n"}.
7#'
8#' @param str A character vector.
9#' @param keepends A logical vector.
10#'
11#' @return A list of character vectors.
12#'
13#' @references \url{https://docs.python.org/3/library/stdtypes.html#str.splitlines}
14#'
15#' @seealso \code{\link{pystr_split}}
16#'
17#' @examples
18#' pystr_splitlines("First\nSecond\rThird\r\n")
19#' pystr_splitlines("First\nSecond\rThird\r\n", TRUE)
20#'
21#' @export
22pystr_splitlines <- function(str, keepends=FALSE) {
23  return(mapply(pystr_splitlines_, str, keepends, SIMPLIFY=FALSE, USE.NAMES=FALSE))
24}
25
26pystr_splitlines_ <- function(str, keepends) {
27  linebreaks = c("\r\n", "\r", "\n")
28  splits = c()
29  remaining = str
30
31  repeat {
32    if(remaining == "") {
33      return(splits)
34    }
35
36    idx = sapply(linebreaks, function(x) pystr_find(remaining, x))
37
38    if(all(idx < 0)) {
39      return(c(splits, remaining))
40    }
41
42    idx = idx[sapply(idx, function(x) x > 0)]
43
44    if(!is.na(idx["\r\n"]) && idx["\r\n"] == min(idx)) {
45      parts = pystr_partition(remaining, "\r\n")[[1]]
46    } else {
47      parts = pystr_partition(remaining, names(sort(idx)[1]))[[1]]
48    }
49
50    if(keepends) {
51      splits = c(splits, paste0(parts[1], parts[2]))
52    } else {
53      splits = c(splits, parts[1])
54    }
55
56    remaining = parts[3]
57  }
58}
59