1 2#' Determine the width of the console 3#' 4#' It uses the `cli.width` option, if set. Otherwise it tries to 5#' determine the size of the terminal or console window. 6#' 7#' These are the exact rules: 8#' * If the `cli.width` option is set to a positive integer, it is used. 9#' * If the `cli.width` option is set, but it is not a positive integer, 10#' and error is thrown. 11#' 12#' Then we try to determine the size of the terminal or console window: 13#' * If we are not in RStudio, or we are in an RStudio terminal, 14#' then we try to use the `tty_size()` function to query the 15#' terminal size. This might fail if R is not running in a terminal, 16#' but failures are ignored. 17#' * If we are in the RStudio build pane, then the `RSTUDIO_CONSOLE_WIDTH` 18#' environment variable is used. If the build pane is resized, then this 19#' environment variable is not accurate any more, and the output might 20#' get garbled. 21#' * We are _not_ using the `RSTUDIO_CONSOLE_WIDTH` environment variable 22#' if we are in the RStudio console. 23#' 24#' If we cannot determine the size of the terminal or console window, then 25#' we use the `width` option. If the `width` option is not set, then 26#' we return 80L. 27#' 28#' @return Integer scalar, the console with, in number of characters. 29#' 30#' @export 31#' @examples 32#' console_width() 33 34console_width <- function() { 35 # cli.width option always takes priotity 36 cwopt <- getOption("cli.width") 37 if (!is.null(cwopt)) { 38 if (!is.numeric(cwopt)) stop("options(\"cli.width\") must be integer") 39 if (length(cwopt) != 1) stop("options(\"cli.width\") must be a scalar") 40 if (is.na(cwopt)) stop("options(\"cli.width\") cannot be NA") 41 if (cwopt == Inf) { 42 cwopti <- .Machine$integer.max 43 } else { 44 cwopti <- as.integer(cwopt) 45 } 46 if (cwopti <= 0) stop("options(\"cli.width\") must be a positive integer") 47 return(cwopti) 48 } 49 50 # detect if in RStudio 51 rs <- rstudio$detect() 52 if (rs$type == "not_rstudio") { 53 # maybe a terminal? 54 width <- terminal_width() 55 56 } else if (rs$type == "rstudio_console_starting") { 57 # there isn't much we can do here, options and env vars are not set 58 width <- NULL 59 60 } else if (rs$type == "rstudio_console") { 61 # will just use getOption("width"), in case the user changed it, 62 # and ignore the RSTUDIO_CONSOLE_WIDTH env var 63 width <- NULL 64 65 } else if (rs$type == "rstudio_build_pane") { 66 # RStudio explicitly sets this for build pane processes 67 # It is only good when the build starts, but we cannot do better 68 width <- rs_console_width() 69 70 } else if (rs$type == "rstudio_terminal") { 71 # Can also be a subprocess of the terminal, with a pty, 72 # but that's fine, the pty should have a width set up. 73 # We do not fall back to the RSTUDIO_CONSOLE_WIDTH env var, 74 # because the user might have changed options("width") and the env 75 # var is only good when the terminal starts, anyway. 76 width <- terminal_width() 77 78 } else { # rstudio_subprocess 79 width <- NULL 80 } 81 82 # If not set, then use the option 83 width <- width %||% getOption("width") %||% 80L 84 85 width 86} 87 88tty_size <- function() { 89 tryCatch( 90 ret <- .Call(clic_tty_size), 91 error = function(err) { 92 class(err) <- c("ps_unknown_tty_size", class(err)) 93 stop(err) 94 } 95 ) 96 c(width = ret[1], height = ret[2]) 97} 98 99terminal_width <- function() { 100 w <- tryCatch(tty_size()[["width"]], error = function(e) NULL) 101 # this is probably a pty that does not set the width, use st sensible 102 if (!is.null(w) && w == 0) w <- 80L 103 w 104} 105 106rs_console_width <- function() { 107 ev <- as.integer(Sys.getenv("RSTUDIO_CONSOLE_WIDTH", ""))[1] 108 if (!is.na(ev)) ev else NULL 109} 110