## Copyright (C) Brodie Gaslam ## ## This file is part of "fansi - ANSI Control Sequence Aware String Functions" ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 2 or 3 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## Go to for copies of the licenses. library(unitizer) library(fansi) unitizer_sect("Basic wrap", { # We do not want to use `base::strwrap` because it triggers the pcre valgrind # error on heap allocations (this is not a fansi problem) hello.0 <- "hello world this is a lovely day" # identical(strwrap_ctl(hello.0, width=10), strwrap(hello.0, width=10)) strwrap_ctl(hello.0, width=10) hello.1 <- "hello world this is. a lovely day." # identical(strwrap_ctl(hello.1, width=10), strwrap(hello.1, width=10)) strwrap_ctl(hello.1, width=10) hello.2 <- "hello\rworld\rthis is. a lovely day." # identical(strwrap(hello.2, width=10), strwrap_ctl(hello.2, width=10)) strwrap_ctl(hello.2, width=10) hello.3 <- "hello\rworld\nthis is. a lovely\n day." # identical(strwrap(hello.3, width=10), strwrap_ctl(hello.3, width=10)) strwrap_ctl(hello.3, width=10) hello.4 <- " hello world this is a lovely day." # identical(strwrap(hello.4, width=10), strwrap_ctl(hello.4, width=10)) strwrap_ctl(hello.4, width=10) hello.5 <- "hello.\n\n\nworld" # identical(strwrap(hello.5, width=10), strwrap_ctl(hello.5, width=10)) strwrap_ctl(hello.5, width=10) hello.5a <- "hello.\n \n \nworld" # identical(strwrap(hello.5a, width=10), strwrap_ctl(hello.5a, width=10)) strwrap_ctl(hello.5a, width=10) # special preserve of double space hello.6a <- 'hello." there' # identical(strwrap(hello.6a, width=40), strwrap_ctl(hello.6a, width=40)) strwrap_ctl(hello.6a, width=40) hello.6b <- 'hello.\' there' # identical(strwrap(hello.6b, width=40), strwrap_ctl(hello.6b, width=40)) strwrap_ctl(hello.6b, width=40) hello.6c <- 'hello.) there' # identical(strwrap(hello.6c, width=40), strwrap_ctl(hello.6c, width=40)) strwrap_ctl(hello.6c, width=40) }) unitizer_sect("Basic Ansi", { hello2.0 <- paste0("hello ", red, "world ", grn.bg, " this is a lovely", end, "day.") strwrap_ctl(hello2.0, 10) # identical( # strwrap_ctl(strip_ctl(hello2.0, "sgr"), 10), # strwrap(strip_ctl(hello2.0, "sgr"), 10) # ) strwrap_ctl(strip_ctl(hello2.0, "sgr"), 10) # turn off tag generic hello2.1 <- paste0("hello \033[41mworld\033[m how are you today") hello2.2 <- paste0("hello \033[41mworld\033[0m how are you today") strwrap_ctl(hello2.1, 15) # Specific turn off tags - turn off bold and faint hello.bold.faint <- paste0( "hello \033[1mbolded once upon a time\033[22m ", "normal \033[2mfainting in faintness oh no\033[22m normal" ) strwrap_ctl(hello.bold.faint, 10) # Specific turn off tags - blinking hello.blinky <- paste0( "hello \033[5mbliking slowly oh my\033[25m ", "normal \033[6mblinking quickly oh my\033[25m normal" ) strwrap_ctl(hello.blinky, 10) strwrap_ctl(hello.blinky, 10, terminate=FALSE) # simplify hello2.3 <- c(hello2.1, hello2.2) strwrap_ctl(hello2.3, 10, simplify=FALSE) strwrap2_ctl(hello2.3, 10, simplify=FALSE) # closed at end strwrap_ctl("aliquip \033[31mex ea commodo consequat\033[0m\n", 25) }) unitizer_sect("Long Wrap", { # wrap.nrm <- strwrap(strip_ctl(lorem.r.thanks, "sgr"), 40) wrap.csi <- strwrap_ctl(lorem.r.thanks, 40) # # this would take forever under valgrind # identical( # strwrap(strip_ctl(lorem.r.thanks, "sgr"), 40), # strip_ctl(strwrap_ctl(lorem.r.thanks, 40), "sgr") # ) # If this changes run the `identical` check above! nchar(strip_ctl(wrap.csi, "sgr")) # If this changes run the `identical` check above! nchar(wrap.csi) }) unitizer_sect("Other Escapes", { strwrap_ctl("hello \033kworld yohoo", 12) strwrap_ctl("hello \033\nworld yohoo", 12) # c0 escapes should be treated as zero width c0 <- "hello\x1F\x1F\x1F\x1F\x1F\x1F world yohoo" Encoding(c0) <- "UTF-8" strwrap_ctl(c0, 12) # Various different types of warnings strwrap_ctl("hello \033[999mworld", 6) strwrap_ctl("hello \033[31#31mworld", 6) strwrap_ctl("hello \033[999nworld", 6) strwrap_ctl("hello \033[999mworld", 6, warn=FALSE) strwrap_ctl("hello \033[31#31mworld", 6, warn=FALSE) strwrap_ctl("hello \033[999nworld", 6, warn=FALSE) }) unitizer_sect("prefix / initial simple", { # a version of lorem with paragraphs lorem.sentence <- unlist(strsplit_ctl(lorem, "[.]\\K ", perl=TRUE)) lorem.sentence <- gsub(",", ",\n", lorem.sentence, fixed=TRUE) lorem.para <- c( paste0(lorem.sentence[1:2], collapse="\n\n"), paste0(lorem.sentence[3:4], collapse="\n\t\n\t \n") ) # identical( # strwrap_ctl(lorem.para, indent=2), strwrap(lorem.para, indent=2) # ) # identical( # strwrap_ctl(lorem.para, exdent=2), strwrap(lorem.para, exdent=2) # ) # identical( # strwrap_ctl(lorem.para, indent=4, exdent=2), # strwrap(lorem.para, indent=4, exdent=2) # ) strwrap_ctl(lorem.para, indent=2) strwrap_ctl(lorem.para, exdent=2) strwrap_ctl(lorem.para, indent=4, exdent=2) }) unitizer_sect("prefix / initial with ESC", { pre <- "\033[32m+ \033[0m" ini <- "\033[33m> \033[0m" hello.8a <- "hello world yohoo" wrap.csi.2 <- strwrap_ctl(hello.8a, 14, prefix=pre, initial=ini) wrap.csi.2 # wrap.nrm.2 <- strwrap(hello.8a, 14, prefix="+ ", initial="> ") # identical(strip_ctl(wrap.csi.2, "sgr"), wrap.nrm.2) hello.8b <- c(hello.8a, "oh my this has 2 elements") wrap.csi.3 <- strwrap_ctl(hello.8b, 14, prefix=pre, initial=ini) wrap.csi.3 # wrap.nrm.3 <- strwrap(hello.8b, 14, prefix="+ ", initial="> ") # identical(strip_ctl(wrap.csi.3, "sgr"), wrap.nrm.3) }) unitizer_sect("wrap2", { # Examples hello.9a <- "hello\t\033[41mred\033[49m\tworld" strwrap2_ctl(hello.9a, 12) strwrap2_ctl(hello.9a, 12, tabs.as.spaces=TRUE) strwrap2_ctl(hello.9a, 13, tabs.as.spaces=TRUE) strwrap2_ctl(hello.9a, 12, tabs.as.spaces=TRUE, tab.stops=c(6, 12)) r.thanks <- lorem.r.thanks.2 ## Generate colors from the 256 color palette bg <- ceiling(seq_along(r.thanks) / length(r.thanks) * 215) + 16 fg <- ifelse((((bg - 16) %/% 18) %% 2), 30, 37) tpl <- "\033[%d;48;5;%dm%s\033[49m" ## Apply colors to strings and collapse nz <- nzchar(r.thanks) r.thanks[nz] <- sprintf(tpl, fg[nz], bg[nz], r.thanks[nz]) r.col <- paste0(r.thanks, collapse="\n") ## Wrap and display r.wrap <- strwrap2_ctl(r.col, 35, pad.end=" ", wrap.always=TRUE) # writeLines(c("", paste(" ", r.wrap[1:27], " ", r.wrap[28:54]), "")) r.wrap ## Pad paragraph break line when it is colored hello.9b <- "\033[41mhello\n\nworld." strwrap2_ctl(hello.9b, 8, pad.end=" ") strwrap2_ctl(hello.9b, 8, pad.end=0) ## Leading spaces hello.9b <- " \033[41mhello world." strwrap2_ctl(hello.9b, 8, strip.spaces=FALSE) hello.9c <- "\033[41m hello world." ## complex white-space ws.0 <- "hello worldza" ws.1 <- "hello \t worldza" ws.2 <- "hello \n worldza" strwrap2_sgr(ws.0, 7, strip.spaces=FALSE) strwrap2_sgr(ws.0, 7, strip.spaces=FALSE, wrap.always=TRUE) strwrap2_sgr(ws.0, 6, strip.spaces=FALSE) strwrap2_sgr(ws.0, 6, strip.spaces=FALSE, wrap.always=TRUE) strwrap2_sgr(ws.1, 7, strip.spaces=FALSE) strwrap2_sgr(ws.1, 7, strip.spaces=FALSE, wrap.always=TRUE) strwrap2_sgr(ws.1, 6, strip.spaces=FALSE) strwrap2_sgr(ws.1, 6, strip.spaces=FALSE, wrap.always=TRUE) strwrap2_sgr(" hello world ", 7, strip.spaces=FALSE) strwrap2_sgr("\thello\tworld\t", 7, strip.spaces=FALSE) strwrap2_sgr("\nhello\nworld\n", 7, strip.spaces=FALSE) }) unitizer_sect("_sgr", { hello.9e <- c( "hello\t\033[91mworld\033[m\t\033[38;2;31;42;4mworld boohaythere", "asdfasdfasdfasdf" ) ## primarily testing that all args are used correctly. strwrap_sgr(hello.9e, 12, term.cap='256') hello.9f <- "hello \033[91mworld boohaythereyowza" strwrap2_sgr( hello.9f, 12, prefix="++", initial=">>", wrap.always=TRUE, pad.end="~", strip.spaces=FALSE ) strwrap2_sgr( hello.9e, 12, tabs.as.spaces=TRUE, tab.stops=c(6, 12), warn=FALSE, indent=2, exdent=1, prefix="++", initial=">>", term.cap="256", wrap.always=TRUE, simplify=FALSE, pad.end="~", strip.spaces=FALSE ) strwrap_sgr( hello.9e, 12, warn=FALSE, indent=2, exdent=1, prefix="++", initial=">>", term.cap="256", simplify=FALSE ) strwrap_sgr( hello.9e, 12, indent=2, exdent=1, prefix="++", initial=">>", term.cap="256", simplify=FALSE ) }) unitizer_sect("long words", { hello.long <- "\033[31mhelloworld\033[mlongword" strwrap_ctl(hello.long, 8) strwrap2_ctl(hello.long, 8, wrap.always=TRUE) }) unitizer_sect("rare escapes", { hello.border <- c( "hello \033[51mworld woohoo\033[54m woohoo", "hello \033[52mworld woohoo\033[54m woohoo", "hello \033[53mworld woohoo\033[55m woohoo" ) strwrap_ctl(hello.border, 12) strwrap_ctl(hello.border, 12, terminate=FALSE) hello.ideogram <- c( "hello \033[60mworld woohoo\033[65m woohoo", "hello \033[61mworld woohoo\033[65m woohoo", "hello \033[62mworld woohoo\033[65m woohoo", "hello \033[63mworld woohoo\033[65m woohoo", "hello \033[64mworld woohoo\033[65m woohoo" ) strwrap_ctl(hello.ideogram, 12, terminate=FALSE) hello.font <- c( "hello \033[10mworld woohoo\033[10m woohoo", "hello \033[11mworld woohoo\033[10m woohoo", "hello \033[12mworld woohoo\033[10m woohoo", "hello \033[13mworld woohoo\033[10m woohoo", "hello \033[14mworld woohoo\033[10m woohoo", "hello \033[15mworld woohoo\033[10m woohoo", "hello \033[16mworld woohoo\033[10m woohoo", "hello \033[17mworld woohoo\033[10m woohoo", "hello \033[18mworld woohoo\033[10m woohoo", "hello \033[19mworld woohoo\033[10m woohoo" ) strwrap_ctl(hello.font, 12, terminate=FALSE) }) unitizer_sect("term cap and bright", { # default term cap should recognize bright and 256, but not true color. getOption('fansi.term.cap', dflt_term_cap()) hello.bright <- '\033[42mhello \033[103mworld wowza\033[49m' strwrap_ctl(hello.bright, 13) strwrap_ctl(hello.bright, 13, term.cap=character()) hello.255 <- '\033[42mhello \033[48;5;47mworld wowza\033[49m' strwrap_ctl(hello.255, 13) strwrap_ctl(hello.255, 13, term.cap=character()) hello.tru <- '\033[42mhello \033[48;2;7;41;4mworld wowza\033[m' strwrap_ctl(hello.tru, 13, term.cap='truecolor') strwrap_ctl(hello.tru, 13) }) unitizer_sect("corner cases", { strwrap_ctl("a", -1) strwrap2_ctl("a", -1) strwrap2_ctl("a", Inf) strwrap2_ctl("a", NA_real_) strwrap2_ctl("a", NA_integer_) strwrap2_ctl("a", -1, wrap.always=TRUE) strwrap2_ctl("a", 0, wrap.always=TRUE) strwrap2_ctl("a", 1, wrap.always=TRUE) tryCatch( strwrap_ctl("hello world", 6, prefix="\033p"), warning=conditionMessage ) suppressWarnings(strwrap_ctl("hello world", 6, prefix="\033p")) # Invalid inputs (checks in C) tryCatch( strwrap2_ctl("hello world", 8, pad.end='\t'), error=conditionMessage ) tryCatch( strwrap2_ctl("hello world", 8, pad.end=' '), error=conditionMessage ) strwrap2_ctl("goodbye moon", 8, indent=5, prefix='> hello >') strwrap2_ctl( "goodbye moon", 16, indent=5, prefix='> hello >', wrap.always=TRUE ) tryCatch( strwrap2_ctl( "goodbye moon", 15, indent=5, prefix='> hello >', wrap.always=TRUE ), error=conditionMessage ) ## Test mixing of SGR with non SGR escapes; non-SGR should not be ## dropped, and _trailing_ SGR should be dropped. strwrap_ctl("hello world\033[31m\033A", 12) strwrap_ctl("hello world\033A\033[31m", 12) # Islanded SGR escape sequence strwrap_ctl("hello \033[44m world", 5) strwrap_ctl("hello \033[44m world", 6) strwrap_ctl("hello \033[44m world", 5, terminate=FALSE) strwrap_ctl("hello \033[44m world", 6, terminate=FALSE) strwrap_ctl("hello\n\033[44m\nworld", 5) strwrap_ctl("hello \n\033[44m\n world", 5) strwrap_ctl("hello \n \033[44m\n world", 5) strwrap_ctl("hello \n \n\033[44mworld", 5) strwrap_ctl("hello \n \n\033[44m world", 5) strwrap_ctl("hello \n \n\033[44m\nworld", 5) strwrap_ctl("hello \033[44m\n\n world", 5) strwrap("hello \n\n world", 5) ## Trailing SGR followed by word break strwrap_ctl("\033[33mAB\033[44m CD", 3) ## New paragraph with wrap.always and trailing SGR strwrap2_ctl("AB\033[44m\n\nCD", 3, wrap.always=TRUE) strwrap2_ctl("AB\033[44m\n\nCD", 3, wrap.always=TRUE, pad.end="#") strwrap2_ctl("AB\033[44m\n\nCD", 3, wrap.always=TRUE, terminate=FALSE) ## Don't omit trail SGR when there is padding strwrap2_ctl("AB\033[44m CD", 4, pad.end="#") strwrap2_ctl("AB\033[44m CD", 3, pad.end="#") ## Combine Leading SGR without stripping spaces strwrap2_sgr("\033[43mAB \033[34mCD", strip.spaces=FALSE, 4) ## Nothing bug sgrs strwrap_ctl("\033[31m\033[43m", 5) ## Hard wrap with trailing SGR strwrap2_ctl("a\033[31mb", 2, wrap.always = TRUE) ## Correctly strip trailing space strwrap_ctl("A \033[31mB\033[39m", 3) ## Don't double warn w/ leading strip strwrap2_ctl("\033[35phello \033[35p world", 5, strip.spaces=FALSE) ## NA treatment identical( strwrap(c(NA, "a b"), 4, prefix=">"), strwrap_ctl(c(NA, "a b"), 4, prefix=">") ) identical(strwrap("a b", 4, prefix=NA), strwrap_ctl("a b", 4, prefix=NA)) }) unitizer_sect("bad inputs", { strwrap_ctl(1:3) strwrap_ctl(hello2.0, width="35") strwrap_ctl(hello2.0, width=NA_integer_) strwrap_ctl(hello2.0, indent=NA_integer_) strwrap_ctl(hello2.0, indent=-3) strwrap_ctl(hello2.0, exdent=-3) strwrap_ctl(hello2.0, exdent=1:3) strwrap_ctl(hello2.0, prefix=1:3) strwrap_ctl(hello2.0, initial=1:3) strwrap_ctl(hello2.0, warn=NULL) strwrap_ctl(hello2.0, term.cap=1:3) strwrap_ctl(hello2.0, term.cap="bananas") strwrap_ctl(hello2.0, ctl=1:3) strwrap_ctl(hello2.0, ctl="bananas") strwrap2_ctl(hello2.0, wrap.always=1:3) strwrap2_ctl(hello2.0, wrap.always=NA) strwrap2_ctl(hello2.0, tabs.as.spaces=NA) strwrap2_ctl(hello2.0, tabs.as.spaces=1) strwrap2_ctl(hello2.0, tab.stops=-(1:3)) strwrap2_ctl(hello2.0, tab.stops=0) strwrap2_ctl(hello2.0, strip.spaces=1:3) strwrap2_ctl(hello2.0, tabs.as.spaces=TRUE, strip.spaces=TRUE) strwrap2_ctl(hello2.0, pad.end=letters) bytes <- "\xf0\xe3" Encoding(bytes) <- "bytes" strwrap_ctl(hello2.0, prefix=bytes) strwrap_ctl(hello2.0, initial=bytes) strwrap2_ctl(hello2.0, pad.end=bytes) strwrap_ctl(c(hello2.0, bytes)) })