Skip to content

Commit 46e8534

Browse files
CLEANUP: split up handlers into separate R files
1 parent b2a9ec9 commit 46e8534

25 files changed

+714
-747
lines changed

R/ascii_alert_handler.R

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#' Auditory Progression Feedback
2+
#'
3+
#' A progression handler based on `cat("\a", file=stderr())`.
4+
#'
5+
#' @inheritParams make_progression_handler
6+
#'
7+
#' @param symbol (character string) The character symbol to be outputted,
8+
#' which by default is the ASCII BEL character (`'\a'` = `'\007'`) character.
9+
#'
10+
#' @param file (connection) A [base::connection] to where output should be sent.
11+
#'
12+
#' @param \ldots Additional arguments passed to [make_progression_handler()].
13+
#'
14+
#' @example incl/ascii_alert_handler.R
15+
#'
16+
#' @export
17+
ascii_alert_handler <- function(symbol = "\a", file = stderr(), intrusiveness = getOption("progressr.intrusiveness.auditory", 5.0), target = c("terminal", "audio"), ...) {
18+
reporter <- local({
19+
list(
20+
update = function(config, state, progression, ...) {
21+
if (state$enabled && progression$amount != 0) cat(file = file, symbol)
22+
}
23+
)
24+
})
25+
26+
make_progression_handler("ascii_alert", reporter, intrusiveness = intrusiveness, ...)
27+
}

R/beepr_handler.R

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#' Auditory Progression Feedback
2+
#'
3+
#' A progression handler for [beepr::beep()].
4+
#'
5+
#' @inheritParams make_progression_handler
6+
#'
7+
#' @param initiate,update,finish (integer) Indices of [beepr::beep()] sounds to
8+
#' play when progress starts, is updated, and completes. For silence, use `NA_integer_`.
9+
#'
10+
#' @param \ldots Additional arguments passed to [make_progression_handler()].
11+
#'
12+
#' @example incl/beepr_handler.R
13+
#'
14+
#' @section Requirements:
15+
#' This progression handler requires the \pkg{beepr} package.
16+
#'
17+
#' @export
18+
beepr_handler <- function(initiate = 2L, update = 10L, finish = 11L, intrusiveness = getOption("progressr.intrusiveness.auditory", 5.0), target = "audio", ...) {
19+
## Used for package testing purposes only when we want to perform
20+
## everything except the last part where the backend is called
21+
if (!is_fake("beepr_handler")) {
22+
beepr_beep <- beepr::beep
23+
} else {
24+
beepr_beep <- function(sound, expr) NULL
25+
}
26+
27+
beep <- function(sound) {
28+
## Silence?
29+
if (is.na(sound)) return()
30+
beepr_beep(sound)
31+
}
32+
33+
## Reporter state
34+
reporter <- local({
35+
list(
36+
initiate = function(config, state, progression, ...) {
37+
if (!state$enabled || config$times == 1L) return()
38+
beep(initiate)
39+
},
40+
41+
update = function(config, state, progression, ...) {
42+
if (!state$enabled || progression$amount == 0 || config$times <= 2L) return()
43+
beep(update)
44+
},
45+
46+
finish = function(config, state, progression, ...) {
47+
if (!state$enabled) return()
48+
beep(finish)
49+
}
50+
)
51+
})
52+
53+
make_progression_handler("beepr", reporter, intrusiveness = intrusiveness, ...)
54+
}

R/debug_handler.R

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#' Textual Progression Feedback for Debug Purposes
2+
#'
3+
#' @inheritParams make_progression_handler
4+
#'
5+
#' @param \ldots Additional arguments passed to [make_progression_handler()].
6+
#'
7+
#' @example incl/debug_handler.R
8+
#'
9+
#' @export
10+
debug_handler <- function(interval = getOption("progressr.interval", 0), intrusiveness = getOption("progressr.intrusiveness.debug", 0), target = "terminal", ...) {
11+
reporter <- local({
12+
t_init <- NULL
13+
14+
add_to_log <- function(config, state, progression, ...) {
15+
t <- Sys.time()
16+
if (is.null(t_init)) t_init <<- t
17+
dt <- difftime(t, t_init, units = "secs")
18+
delay <- difftime(t, progression$time, units = "secs")
19+
message <- paste(c(state$message, ""), collapse = "")
20+
entry <- list(now(t), dt, delay, progression$type, state$step, config$max_steps, state$delta, message, config$clear, state$enabled, paste0(progression$status, ""))
21+
msg <- do.call(sprintf, args = c(list("%s(%.3fs => +%.3fs) %s: %d/%d (%+d) '%s' {clear=%s, enabled=%s, status=%s}"), entry))
22+
message(msg)
23+
}
24+
25+
list(
26+
reset = function(...) {
27+
t_init <<- NULL
28+
},
29+
30+
initiate = function(...) {
31+
add_to_log("initiate", ...)
32+
},
33+
34+
update = function(...) {
35+
add_to_log("update", ...)
36+
},
37+
38+
finish = function(...) {
39+
add_to_log("finish", ...)
40+
}
41+
)
42+
})
43+
44+
make_progression_handler("debug", reporter, intrusiveness = intrusiveness, ...)
45+
}

R/filesize_handler.R

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#' Progression Updates Reflected as the Size of a File
2+
#'
3+
#' @inheritParams make_progression_handler
4+
#'
5+
#' @param file (character) A filename.
6+
#'
7+
#' @param \ldots Additional arguments passed to [make_progression_handler()].
8+
#'
9+
#' @examples
10+
#' \donttest{\dontrun{
11+
#' handlers(filesize_handler(file = "myscript.progress"))
12+
#' with_progress(y <- slow_sum(1:100))
13+
#' }}
14+
#'
15+
#' @importFrom utils file_test
16+
#' @export
17+
filesize_handler <- function(file = "default.progress", intrusiveness = getOption("progressr.intrusiveness.file", 5), target = "file", ...) {
18+
reporter <- local({
19+
set_file_size <- function(config, state, progression) {
20+
ratio <- state$step / config$max_steps
21+
size <- round(100 * ratio)
22+
current_size <- file.size(file)
23+
if (is.na(current_size)) file.create(file, showWarnings = FALSE)
24+
if (size == 0L) return()
25+
if (progression$amount == 0) return()
26+
27+
head <- sprintf("%g/%g: ", state$step, config$max_steps)
28+
nhead <- nchar(head)
29+
tail <- sprintf(" [%d%%]", round(100 * ratio))
30+
ntail <- nchar(tail)
31+
mid <- paste0(state$message, "")
32+
nmid <- nchar(mid)
33+
padding <- size - (nhead + nmid + ntail)
34+
if (padding <= 0) {
35+
msg <- paste(head, mid, tail, sep = "")
36+
if (padding < 0) msg <- substring(msg, first = 1L, last = size)
37+
} else if (padding > 0) {
38+
mid <- paste(c(mid, " ", rep(".", times = padding - 1L)), collapse = "")
39+
msg <- paste(head, mid, tail, sep = "")
40+
}
41+
42+
cat(file = file, append = FALSE, msg)
43+
}
44+
45+
list(
46+
initiate = function(config, state, progression, ...) {
47+
set_file_size(config = config, state = state, progression = progression)
48+
},
49+
50+
update = function(config, state, progression, ...) {
51+
set_file_size(config = config, state = state, progression = progression)
52+
},
53+
54+
finish = function(config, state, progression, ...) {
55+
if (config$clear) {
56+
if (file_test("-f", file)) file.remove(file)
57+
} else {
58+
set_file_size(config = config, state = state, progression = progression)
59+
}
60+
}
61+
)
62+
})
63+
64+
make_progression_handler("filesize", reporter, intrusiveness = intrusiveness, ...)
65+
}

R/newline_handler.R

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#' Textual Progression Feedback that outputs a Newline
2+
#'
3+
#' @inheritParams make_progression_handler
4+
#'
5+
#' @param symbol (character string) The character symbol to be outputted,
6+
#' which by default is the ASCII NL character (`'\n'` = `'\013'`) character.
7+
#'
8+
#' @param file (connection) A [base::connection] to where output should be sent.
9+
#'
10+
#' @param \ldots Additional arguments passed to [make_progression_handler()].
11+
#'
12+
#' @export
13+
newline_handler <- function(symbol = "\n", file = stderr(), intrusiveness = getOption("progressr.intrusiveness.debug", 0), target = "terminal", ...) {
14+
reporter <- local({
15+
list(
16+
initiate = function(...) cat(file = file, symbol),
17+
update = function(...) cat(file = file, symbol),
18+
finish = function(...) cat(file = file, symbol)
19+
)
20+
})
21+
22+
make_progression_handler("newline", reporter, intrusiveness = intrusiveness, ...)
23+
}

R/notifier_handler.R

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#' Operating-System Specific Progression Feedback
2+
#'
3+
#' A progression handler for [notifier::notify()].
4+
#'
5+
#' @inheritParams make_progression_handler
6+
#'
7+
#' @param \ldots Additional arguments passed to [make_progression_handler()].
8+
#'
9+
#' @example incl/notifier_handler.R
10+
#'
11+
#' @section Requirements:
12+
#' This progression handler requires the \pkg{notifier} package, which is only
13+
#' available from <https://github.com/gaborcsardi/notifier>. This can be
14+
#' installed as `remotes::install_github("gaborcsardi/notifier@62d484")`.
15+
#'
16+
#' @export
17+
notifier_handler <- function(intrusiveness = getOption("progressr.intrusiveness.notifier", 10), target = "gui", ...) {
18+
## Used for package testing purposes only when we want to perform
19+
## everything except the last part where the backend is called
20+
if (!is_fake("notifier_handler")) {
21+
notifier_notify <- function(...) notifier::notify(...)
22+
} else {
23+
notifier_notify <- function(...) NULL
24+
}
25+
26+
notify <- function(step, max_steps, message) {
27+
ratio <- sprintf("%.0f%%", 100*step/max_steps)
28+
msg <- paste(c("", message), collapse = "")
29+
notifier_notify(sprintf("[%s] %s", ratio, msg))
30+
}
31+
32+
reporter <- local({
33+
finished <- FALSE
34+
35+
list(
36+
reset = function(...) {
37+
finished <<- FALSE
38+
},
39+
40+
initiate = function(config, state, progression, ...) {
41+
if (!state$enabled || config$times == 1L) return()
42+
notify(step = state$step, max_steps = config$max_steps, message = state$message)
43+
},
44+
45+
update = function(config, state, progression, ...) {
46+
if (!state$enabled || progression$amount == 0 || config$times <= 2L) return()
47+
notify(step = state$step, max_steps = config$max_steps, message = state$message)
48+
},
49+
50+
finish = function(config, state, progression, ...) {
51+
if (finished) return()
52+
if (!state$enabled) return()
53+
if (state$delta > 0) notify(step = state$step, max_steps = config$max_steps, message = state$message)
54+
finished <<- TRUE
55+
}
56+
)
57+
})
58+
59+
make_progression_handler("notifier", reporter, intrusiveness = intrusiveness, ...)
60+
}

R/pbmcapply_handler.R

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#' Visual Progression Feedback
2+
#'
3+
#' A progression handler for [pbmcapply::progressBar()].
4+
#'
5+
#' @inheritParams make_progression_handler
6+
#'
7+
#' @param substyle (integer) The progress-bar substyle according to [pbmcapply::progressBar()].
8+
#'
9+
#' @param style (character) The progress-bar style according to [pbmcapply::progressBar()].
10+
#'
11+
#' @param file (connection) A [base::connection] to where output should be sent.
12+
#'
13+
#' @param \ldots Additional arguments passed to [make_progression_handler()].
14+
#'
15+
#' @example incl/pbmcapply_handler.R
16+
#'
17+
#' @section Requirements:
18+
#' This progression handler requires the \pkg{pbmcapply} package.
19+
#'
20+
#' @importFrom utils file_test flush.console txtProgressBar setTxtProgressBar
21+
#' @export
22+
pbmcapply_handler <- function(substyle = 3L, style = "ETA", file = stderr(), intrusiveness = getOption("progressr.intrusiveness.terminal", 1), target = "terminal", ...) {
23+
if (!is_fake("pbmcapply_handler")) {
24+
progressBar <- pbmcapply::progressBar
25+
eraseTxtProgressBar <- function(pb) {
26+
pb_env <- environment(pb$getVal)
27+
with(pb_env, {
28+
style_eta <- exists(".time0", inherits = FALSE)
29+
if (!style_eta) {
30+
if (style == 1L || style == 2L) {
31+
n <- .nb
32+
} else if (style == 3L) {
33+
n <- 3L + nw * width + 6L
34+
}
35+
} else {
36+
## FIXME: Seems to work; if not, see pbmcapply:::txtProgressBarETA()
37+
n <- width
38+
}
39+
cat("\r", strrep(" ", times = n), "\r", sep = "", file = file)
40+
flush.console()
41+
})
42+
}
43+
} else {
44+
progressBar <- function(..., style, substyle) txtProgressBar(..., style = substyle)
45+
setTxtProgressBar <- function(...) NULL
46+
eraseTxtProgressBar <- function(pb) NULL
47+
}
48+
49+
reporter <- local({
50+
## Import functions
51+
52+
pb <- NULL
53+
54+
make_pb <- function(...) {
55+
if (!is.null(pb)) return(pb)
56+
pb <<- progressBar(...)
57+
pb
58+
}
59+
60+
list(
61+
reset = function(...) {
62+
pb <<- NULL
63+
},
64+
65+
initiate = function(config, state, progression, ...) {
66+
if (!state$enabled || config$times == 1L) return()
67+
make_pb(max = config$max_steps, style = style, substyle = substyle, file = file)
68+
},
69+
70+
update = function(config, state, progression, ...) {
71+
if (!state$enabled || progression$amount == 0 || config$times <= 2L) return()
72+
make_pb(max = config$max_steps, style = style, substyle = substyle, file = file)
73+
setTxtProgressBar(pb, value = state$step)
74+
},
75+
76+
finish = function(config, state, progression, ...) {
77+
## Already finished?
78+
if (is.null(pb)) return()
79+
if (!state$enabled) return()
80+
if (config$clear) {
81+
eraseTxtProgressBar(pb)
82+
## Suppress newline outputted by close()
83+
pb_env <- environment(pb$getVal)
84+
file <- pb_env$file
85+
pb_env$file <- tempfile()
86+
on.exit({
87+
if (file_test("-f", pb_env$file)) file.remove(pb_env$file)
88+
pb_env$file <- file
89+
})
90+
} else {
91+
setTxtProgressBar(pb, value = state$step)
92+
}
93+
close(pb)
94+
pb <<- NULL
95+
}
96+
)
97+
})
98+
99+
make_progression_handler("pbmcapply", reporter, intrusiveness = intrusiveness, ...)
100+
}

0 commit comments

Comments
 (0)