@@ -15,10 +15,12 @@ transform_utf8 <- function(path, fun, write_back = TRUE) {
1515
1616# ' @importFrom rlang with_handlers warn
1717transform_utf8_one <- function (path , fun , write_back ) {
18- old <- xfun :: read_utf8(path )
1918 with_handlers({
20- new <- fun(old )
21- identical <- identical(unclass(old ), unclass(new ))
19+ file_with_info <- read_utf8(path )
20+ # only write back when changed OR when there was a missing newline
21+ new <- fun(file_with_info $ text )
22+ identical_content <- identical(unclass(file_with_info $ text ), unclass(new ))
23+ identical <- identical_content && ! file_with_info $ missing_EOF_line_break
2224 if (! identical && write_back ) {
2325 xfun :: write_utf8(new , path )
2426 }
@@ -28,3 +30,58 @@ transform_utf8_one <- function(path, fun, write_back) {
2830 NA
2931 })
3032}
33+
34+ # ' Read UTF-8
35+ # '
36+ # ' Reads an UTF-8 file, returning the content and whether or not the final line
37+ # ' was blank. This information is required higher up in the callstack because
38+ # ' we should write back if contents changed or if there is no blank line at the
39+ # ' EOF. A perfectly styled file with no EOF blank line will gain such a line
40+ # ' with this implementation.
41+ # ' @param path A path to a file to read.
42+ # ' @keywords internal
43+ read_utf8 <- function (path ) {
44+ out <- rlang :: with_handlers(
45+ read_utf8_bare(path ),
46+ warning = function (w ) w ,
47+ error = function (e ) e
48+ )
49+ if (inherits(out , " character" )) {
50+ list (
51+ text = out ,
52+ missing_EOF_line_break = FALSE
53+ )
54+ } else if (inherits(out , " error" )) {
55+ rlang :: abort(out $ message )
56+ } else if (inherits(out , " warning" )) {
57+ list (
58+ text = read_utf8_bare(path , warn = FALSE ),
59+ missing_EOF_line_break = grepl(" incomplete" , out $ message )
60+ )
61+ }
62+ }
63+
64+ # ' Drop-in replacement for [xfun::read_utf8()], with an optional `warn`
65+ # ' argument.
66+ # ' @keywords internal
67+ read_utf8_bare <- function (con , warn = TRUE ) {
68+ x <- readLines(con , encoding = " UTF-8" , warn = warn )
69+ i <- invalid_utf8(x )
70+ n <- length(i )
71+ if (n > 0 ) {
72+ stop(
73+ c(
74+ " The file " , con , " is not encoded in UTF-8. " ,
75+ " These lines contain invalid UTF-8 characters: "
76+ ),
77+ paste(c(head(i ), if (n > 6 ) " ..." ), collapse = " , " )
78+ )
79+ }
80+ x
81+ }
82+
83+ # ' Drop-in replacement for [xfun:::invalid_utf8()]
84+ # ' @keywords internal
85+ invalid_utf8 <- function (x ) {
86+ which(! is.na(x ) & is.na(iconv(x , " UTF-8" , " UTF-8" )))
87+ }
0 commit comments