` and `` tags. This function either calls
8 | #' [md_fence()] or [md_indent()] based on the `type` argument.
9 | #'
10 | #' @param x A character vector of lines to be wrapped concatenated into a single
11 | #' block, possibly created by [readLines()] or [deparse()].
12 | #' @param ... Arguments to be passed to [md_fence()] or [md_indent()].
13 | #' @param type The type of code block to be created. Either "tick", "tilde"
14 | #' (which call [md_fence()]) or "indent" (which calls [md_indent()]).
15 | #' @return A `glue` object of length 1, with elements of `x` formatted via
16 | #' [md_fence()] or [md_indent()].
17 | #' @family leaf block functions
18 | #' @examples
19 | #' md_chunk("$ sudo apt install r-base-dev", info = "bash")
20 | #' md_indent(
21 | #' n = c(4, 4, 6),
22 | #' x = c(
23 | #' "library(dplyr)",
24 | #' "starwars %>%",
25 | #' "filter(species == 'Droid')"
26 | #' )
27 | #' )
28 | #' @export
29 | md_chunk <- function(x, type = c("tick", "tilde", "indent"), ...) {
30 | type <- match.arg(type)
31 | if (type == "tick") {
32 | md_fence(x, char = "`", ...)
33 | } else {
34 | if (type == "tilde") {
35 | md_fence(x, char = "~", ...)
36 | } else {
37 | md_indent(x, ...)
38 | }
39 | }
40 | }
41 |
42 | #' Markdown indented code block
43 | #'
44 | #' Turn a character vector of lines into a single code block with each line
45 | #' indented four spaces. This markdown leaf block can be rendered as nested HTML
46 | #' `` and `` tags. This is the code block format required by legacy
47 | #' Reddit-flavored Markdown.
48 | #'
49 | #' @details
50 | #' An indented code block is composed of one or more indented chunks separated
51 | #' by blank lines. An indented chunk is a sequence of non-blank lines, each
52 | #' indented four or more spaces. The contents of the code block are the literal
53 | #' contents of the lines, including trailing line endings, minus four spaces of
54 | #' indentation. An indented code block has no info string.
55 | #' @param x A character vector of lines to be wrapped concatenated into a
56 | #' single block, possibly created by [readLines()] or [deparse()].
57 | #' @param n A numeric vector
58 | #' @return A `glue` object of length 1, with the elements of `x` preceded with
59 | #' 4 spaces and separated by a newline.
60 | #' @family leaf block functions
61 | #' @examples
62 | #' md_indent(deparse(md_bold))
63 | #' @importFrom glue glue glue_collapse
64 | #' @export
65 | md_indent <- function(x, n = 4) {
66 | if (min(n) < 4) {
67 | stop("Indented code blocks must be indented by at least four spaces.")
68 | }
69 | glue::glue("{strrep(' ', n)}{x}")
70 | }
71 |
72 | #' Markdown fenced code block
73 | #'
74 | #' Turn a character vector of lines into a single code block with lines
75 | #' bookended with a code fence of backticks or tildes. This markdown leaf block
76 | #' can be rendered as HTML `` tags inside `` tags.
77 | #'
78 | #' @details
79 | #' A code fence is a sequence of at least three consecutive backtick characters
80 | #' ... or tildes (`~`). (Tildes and backticks cannot be mixed.) A fenced code
81 | #' block begins with a code fence, indented no more than three spaces.
82 | #'
83 | #' The line with the opening code fence may optionally contain some text
84 | #' following the code fence; this is trimmed of leading and trailing whitespace
85 | #' and called the info string...
86 | #'
87 | #' The content of the code block consists of all subsequent lines, until a
88 | #' closing code fence of the same type as the code block began with (backticks
89 | #' or tildes), and with at least as many backticks or tildes as the opening code
90 | #' fence...
91 | #'
92 | #' A fenced code block may interrupt a paragraph, and does not require a blank
93 | #' line either before or after.
94 | #'
95 | #' The content of a code fence is treated as literal text, not parsed as
96 | #' inlines. The first word of the info string is typically used to specify the
97 | #' language of the code sample, and rendered in the class attribute of the code
98 | #' tag. However, this spec does not mandate any particular treatment of the info
99 | #' string (see the `info` argument).
100 | #' @param x A character vector of lines to be wrapped concatenated into a single
101 | #' block, possibly created by possibly created by [readLines()] or
102 | #' [deparse()].
103 | #' @param char The character to use in the code fence; either backtick
104 | #' characters... or tildes (`~`). Defaults to backticks.
105 | #' @param info The info string text to follow the initial code fence, typically
106 | #' a code for the language of the lines of `x`. Defaults to `r`.
107 | #' @return A character vector wrapped on either side by code fences.
108 | #' @family leaf block functions
109 | #' @examples
110 | #' md_fence(deparse(sd))
111 | #' md_fence(c("library(dplyr)", "starwars %>%", " filter(species == 'Droid')"))
112 | #' @importFrom glue glue glue_collapse
113 | #' @export
114 | md_fence <- function(x, char = c("`", "~"), info = "r") {
115 | char <- match.arg(char)
116 | if (!is.null(info)) {
117 | if (char == "`" & grepl("`", info)) {
118 | stop("The info string cannot contain any backtick characters.")
119 | }
120 | }
121 | string <- glue_collapse(x, sep = "\n")
122 | fence <- strrep(char, 3)
123 | info <- if (is.null(info)) {
124 | ""
125 | } else {
126 | info
127 | }
128 | glue::glue("{fence}{info}\n{string}\n{fence}")
129 | }
130 |
--------------------------------------------------------------------------------
/R/md-convert.R:
--------------------------------------------------------------------------------
1 | #' Convert markdown to HTML
2 | #'
3 | #' Take a character vector of valid markdown text and pass it to
4 | #' [markdown::markdownToHTML()] to create a glue vector of HTML fragments.
5 | #' Primarily used to test that `md_*()` functions create vectors that meet the
6 | #' GFM spec and can be rendered as HTML.
7 | #'
8 | #' @details
9 | #' GFM enables the `tagfilter` extension, where the following HTML tags will be
10 | #' filtered when rendering HTML output...
11 | #' @param x A character vector of _markdown_ text to be converted.
12 | #' @param frag logical; Whether only a single HTML fragment should be returned.
13 | #' `TRUE` by default.
14 | #' @param disallow logical; Should [md_disallow()] be called on the converted
15 | #' output?
16 | #' @return A `glue` vector of length 1 containing HTML tags.
17 | #' @family inline functions
18 | #' @examples
19 | #' md_convert(x = md_bold("test"))
20 | #' @importFrom glue as_glue
21 | #' @export
22 | md_convert <- function(x, frag = TRUE, disallow = TRUE) {
23 | if (!has_markdown()) {
24 | stop("Package 'markdown' needed for this function to work.")
25 | } else {
26 | html <- markdown::mark_html(text = x, template = !frag, options = c(
27 | "-smart", "-tasklist"
28 | ))
29 | if (disallow) {
30 | md_disallow(html)
31 | } else {
32 | glue::as_glue(html)
33 | }
34 | }
35 | }
36 |
37 | #' Disallow certain raw HTML
38 | #'
39 | #' Take a character vector of raw HTML text (possibly via [md_convert()]) and
40 | #' disallow certain tags by replacing `<` with `<`.
41 | #'
42 | #' @details
43 | #' GFM enables the tagfilter extension, where the following HTML tags will be
44 | #' filtered when rendering HTML output:
45 | #' * ``
46 | #' * `