R/echo.R

Defines functions echo

echo <- function(
  msg,
  ...,
  indent    = 0,
  sleep     = 0,
  fn_name   = NULL,
  timestamp = FALSE,
  error     = FALSE,
  abort     = FALSE,
  warn      = FALSE,
  fg        = NULL,
  bg        = NULL,
  style     = NULL,
  notify    = FALSE,
  notification = list(
    title         = '',
    subtitle      = NA,
    app_icon      = NA,
    content_image = NA,
    command       = NA,
    open_iterm    = FALSE
    )
  ) {
  # Update console with custom message and optional indent, current datetime and text styles.
  #
  # Arguments:
  #   msg {char} -- message to print to console
  # 
  # Keyword Arguments:
  #   ...          {char} -- additional arguments passed into sprintf()
  #   indent        {int} -- indentation level of message printed to console (default: 0)
  #   sleep     {numeric} -- number of seconds to pause after printing message (default: 0)
  #   fn_name      {char} -- name of function to print in 'ERROR|Warning in function {}'
  #                          (default: NULL)
  #   timestamp {logical} -- if TRUE, print timestamp before message (default: FALSE)
  #   error     {logical} -- if TRUE, print 'ERROR: ' before message (default: FALSE)
  #   abort     {logical} -- if TRUE, print 'ERROR (fatal): ' before message and exit
  #                          program (default: FALSE)
  #   warn      {logical} -- if TRUE, print 'WARNING: ' before message (default: FALSE)
  #   fg           {char} -- foreground color of text (one of: "black", "red", "green",
  #                          "yellow", "blue", "magenta", "cyan", "white", "silver") (default: NULL)
  #   bg           {char} -- background color of text (one of: "bgBlack", "bgRed", "bgGreen",
  #                          "bgYellow", "bgBlue", "bgMagenta", "bgCyan", "bgWhite") (default: NULL)
  #   style        {char} -- text style (one or multiple of of: "bold", "blurred", "italic",
  #                          "underline", "inverse", "hidden", "strikethrough") (default: NULL)
  #   notify    {logical} -- if TRUE, send notification to macOS using macos_notify()
  #   notification {list} -- list containing notification properties, elements:
  #     title         {char} -- title of notification
  #     subtitle      {char} -- subtitle of notification
  #     app_icon      {char} -- path to icon image to display in notification
  #     content_image {char} -- path to content image to display in notification
  #     command       {char} -- shell command to execute upon clicking on notification
  #     open_iterm {logical} -- if TRUE, open "iTerm.app" when notification is clicked
  # 
  # Returns:
  #   nothing
  
  # Save original message as normal character string
  msg_raw = crayon::strip_style(sprintf(msg, ...))
  
  # Check for 'crayon' package for colorized text output
  if (!requireNamespace("crayon", quietly = TRUE)) {
    stop("Package \"crayon\" needed for echo() to work. Please install it.",
      call. = FALSE)
  }
  require(crayon, quietly = TRUE)
  
  # Parse color input parameters
  define_style <- function(fg=NULL, bg=NULL, text_style=NULL) {
    # Define custom text style
    style = list()
    if (!is.null(fg) || !is.null(bg)) {
      if (!is.null(fg)) { style$fg = make_style(fg) }
      if (!is.null(bg)) { style$bg = make_style(bg) }
      custom_style = do.call(combine_styles, style)
    }
    if (!is.null(text_style)) {
      if("reset"         %in% text_style) custom_style = custom_style$reset
      if("bold"          %in% text_style) custom_style = custom_style$bold
      if("blurred"       %in% text_style) custom_style = custom_style$blurred
      if("italic"        %in% text_style) custom_style = custom_style$italic
      if("underline"     %in% text_style) custom_style = custom_style$underline
      if("inverse"       %in% text_style) custom_style = custom_style$inverse
      if("hidden"        %in% text_style) custom_style = custom_style$hidden
      if("strikethrough" %in% text_style) custom_style = custom_style$strikethrough
    }
    if (!is.null(fg) || !is.null(bg)) {
      return(custom_style)
    } else {
      return(NULL)
    }
  }
  style = define_style(fg, bg, text_style=style)
  if (!is.null(style)) { msg = style(msg) }
  
  # Add error colorized text to message
  if (error || abort || warn) {
    style_error = combine_styles(crayon::red, crayon::bold)
    style_warn = combine_styles(crayon::yellow, crayon::bold)
    msg = sprintf("%s%s%s %s",
      ifelse(error || abort, style_error("ERROR"), style_warn("WARNING")),
      ifelse(abort, style_error(" (fatal)"), ""),
      ifelse(
        is.character(fn_name),
        ifelse(error || abort,
          style_error(sprintf(" in function %s():", fn_name)),
          style_warn(sprintf(" in function %s():", fn_name))),
        ifelse(error || abort, style_error(":"), style_warn(":"))),
      msg)
  }
  tab = ifelse(indent==0, "", paste0(rep("  ", indent), collapse = ""))
  msg = paste0(tab, msg)
  if (is.character(fn_name)) {
    color_fn = crayon::silver
    fn_name = sprintf("%s %s%s", color_fn(bold("<fn:")), red(paste0(fn_name)), color_fn(">"))
    msg = paste(fn_name, msg)
  }
  if (timestamp) {
    ts = cyan(paste0(Sys.time(), " "))
    msg = paste0(ts, msg)
  }
  cat(sprintf(msg, ...), "\n")
  if (sleep > 0) { Sys.sleep(sleep) }
  if (abort) {
    stop_quietly <- function() {
      opt <- options(show.error.messages = FALSE)
      on.exit(options(opt))
      stop()
    }
    stop_quietly()
  }
  
  # Notify with macos_notify() if specified
  if (notify) {
    if (exists("macos_notify")) {
      # Assign default values if only partial notification elements supplied
      # Ex. If a user supplies notification = list(title = "test title"), then all other
      # notification elements (subtitle, app_icon, ...) will be NULL. Assign these NULL
      # values to default.
      notification$title         = ifelse(is.null(notification$title), "", notification$title)
      notification$subtitle      = ifelse(is.null(notification$subtitle), NA, notification$subtitle)
      notification$app_icon      = ifelse(is.null(notification$app_icon), NA, notification$app_icon)
      notification$content_image = ifelse(is.null(notification$content_image), NA, notification$content_image)
      notification$command       = ifelse(is.null(notification$command), NA, notification$command)
      notification$open_iterm    = ifelse(is.null(notification$open_iterm), FALSE, notification$open_iterm)
      
      macos_notify(
        title         = notification$title,
        subtitle      = notification$subtitle,
        message       = msg_raw,
        app_icon      = notification$app_icon,
        content_image = notification$content_image,
        command       = notification$command,
        open_iterm    = notification$open_iterm)
    } else {
      stop("'notify' parameter is TRUE but macos_notify() function can't be found!")
    }
  }
}
tsouchlarakis/rdoni documentation built on Sept. 16, 2019, 8:53 p.m.