├── .Rbuildignore ├── .covrignore ├── .github ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── SUPPORT.md └── workflows │ ├── check-standard.yaml │ ├── pkgdown.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── gluedown-package.R ├── md-autolink.R ├── md-chunk.R ├── md-convert.R ├── md-escape.R ├── md-heading.R ├── md-inlines.R ├── md-issue.R ├── md-lines.R ├── md-link.R ├── md-list.R ├── md-quote.R ├── md-rule.R ├── md-table.R ├── md-text.R └── utils.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── codecov.yml ├── cran-comments.md ├── gluedown.Rproj ├── inst └── WORDLIST ├── man ├── figures │ └── logo.png ├── gluedown-package.Rd ├── has_knitr.Rd ├── md_autolink.Rd ├── md_blank.Rd ├── md_bold.Rd ├── md_bullet.Rd ├── md_chunk.Rd ├── md_code.Rd ├── md_convert.Rd ├── md_disallow.Rd ├── md_escape.Rd ├── md_fence.Rd ├── md_hardline.Rd ├── md_heading.Rd ├── md_image.Rd ├── md_indent.Rd ├── md_issue.Rd ├── md_italic.Rd ├── md_label.Rd ├── md_link.Rd ├── md_list.Rd ├── md_order.Rd ├── md_paragraph.Rd ├── md_quote.Rd ├── md_reference.Rd ├── md_rule.Rd ├── md_setext.Rd ├── md_softline.Rd ├── md_strike.Rd ├── md_table.Rd ├── md_task.Rd ├── md_text.Rd └── pipe.Rd ├── pkgdown └── favicon │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── favicon.ico ├── tests ├── spelling.R ├── testthat.R └── testthat │ ├── test-4.1-thematic-breaks.R │ ├── test-4.10-tables.R │ ├── test-4.2-atx-headings.R │ ├── test-4.3-setext-headings.R │ ├── test-4.4-indented-code.R │ ├── test-4.5-fenced-code.R │ ├── test-4.7-link-references.R │ ├── test-4.8-paragraphs.R │ ├── test-4.9-blank-lines.R │ ├── test-5.1-block-quotes.R │ ├── test-5.3-task-list.R │ ├── test-5.4-lists.R │ ├── test-6.1-backslash-escape.R │ ├── test-6.10-raw-html.R │ ├── test-6.11-disallow-raw-html.R │ ├── test-6.12-hard-line-breaks.R │ ├── test-6.13-soft-line-breaks.R │ ├── test-6.14-textual-content.R │ ├── test-6.3-code-spans.R │ ├── test-6.4-emphasis.R │ ├── test-6.5-strikethrough.R │ ├── test-6.6-links.R │ ├── test-6.7-images.R │ ├── test-6.8-autolinks.R │ ├── test-generic-functions.R │ └── test-github-issue.R └── vignettes ├── .gitignore ├── github-spec.Rmd └── literal-programming.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^gluedown\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^README\.Rmd$ 5 | ^cran-comments\.md$ 6 | ^_pkgdown\.yml$ 7 | ^docs$ 8 | ^pkgdown$ 9 | ^\.travis\.yml$ 10 | ^codecov\.yml$ 11 | ^\.covrignore$ 12 | ^\.github$ 13 | ^data-raw$ 14 | ^CRAN-RELEASE$ 15 | ^CRAN-SUBMISSION$ 16 | -------------------------------------------------------------------------------- /.covrignore: -------------------------------------------------------------------------------- 1 | R/deprec-*.R 2 | R/compat-*.R 3 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | 78 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to gluedown 2 | 3 | This outlines how to propose a change to gluedown. For more detailed 4 | info about contributing to this, and other tidyverse packages, please see the 5 | [**development contributing guide**](https://rstd.io/tidy-contrib). 6 | 7 | ### Fixing typos 8 | 9 | Small typos or grammatical errors in documentation may be edited directly using 10 | the GitHub web interface, so long as the changes are made in the _source_ file. 11 | 12 | * YES: you edit a roxygen comment in a `.R` file below `R/`. 13 | * NO: you edit an `.Rd` file below `man/`. 14 | 15 | ### Prerequisites 16 | 17 | Before you make a substantial pull request, you should always file an issue and 18 | make sure someone from the team agrees that it’s a problem. If you’ve found a 19 | bug, create an associated issue and illustrate the bug with a minimal 20 | [reprex](https://www.tidyverse.org/help/#reprex). 21 | 22 | ### Pull request process 23 | 24 | * We recommend that you create a Git branch for each pull request (PR). 25 | * Look at the Travis and AppVeyor build status before and after making changes. 26 | The `README` should contain badges for any continuous integration services used 27 | by the package. 28 | * New code should follow the tidyverse [style guide](https://style.tidyverse.org). 29 | You can use the [styler](https://CRAN.R-project.org/package=styler) package to 30 | apply these styles, but please don't restyle code that has nothing to do with 31 | your PR. 32 | * We use [roxygen2](https://cran.r-project.org/package=roxygen2), with 33 | [Markdown syntax](https://cran.r-project.org/web/packages/roxygen2/vignettes/markdown.html), 34 | for documentation. 35 | * We use [testthat](https://cran.r-project.org/package=testthat). Contributions 36 | with test cases included are easier to accept. 37 | * For user-facing changes, add a bullet to the top of `NEWS.md` below the 38 | current development version header describing the changes made followed by your 39 | GitHub username, and links to relevant issue(s)/PR(s). 40 | 41 | ### Code of Conduct 42 | 43 | Please note that the gluedown project is released with a 44 | [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By contributing to this 45 | project you agree to abide by its terms. 46 | 47 | ### See tidyverse [development contributing guide](https://rstd.io/tidy-contrib) 48 | for further details. 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please briefly describe your problem and what output you expect. If you have a question, please don't use this form. Instead, ask on or . 2 | 3 | Please include a minimal reproducible example (AKA a reprex). If you've never heard of a [reprex](https://reprex.tidyverse.org/) before, start by reading . 4 | 5 | --- 6 | 7 | Brief description of the problem 8 | 9 | ```r 10 | reprex::reprex({ 11 | #+ setup, echo=FALSE 12 | knitr::opts_chunk$set(results = 'asis') 13 | 14 | #+ actual-reprex-code below 15 | 16 | }) 17 | ``` 18 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Getting help with gluedown 2 | 3 | Thanks for using gluedown. Before filing an issue, there are a few places 4 | to explore and pieces to put together to make the process as smooth as possible. 5 | 6 | Start by making a minimal **repr**oducible **ex**ample using the 7 | [reprex](https://reprex.tidyverse.org/) package. If you haven't heard of or used 8 | reprex before, you're in for a treat! Seriously, reprex will make all of your 9 | R-question-asking endeavors easier (which is a pretty insane ROI for the five to 10 | ten minutes it'll take you to learn what it's all about). For additional reprex 11 | pointers, check out the [Get help!](https://www.tidyverse.org/help/) section of 12 | the tidyverse site. 13 | 14 | Armed with your reprex, the next step is to figure out [where to ask](https://www.tidyverse.org/help/#where-to-ask). 15 | 16 | * If it's a question: start with [community.rstudio.com](https://community.rstudio.com/), 17 | and/or StackOverflow. There are more people there to answer questions. 18 | * If it's a bug: you're in the right place, file an issue. 19 | * If you're not sure: let the community help you figure it out! If your 20 | problem _is_ a bug or a feature request, you can easily return here and 21 | report it. 22 | 23 | Before opening a new issue, be sure to [search issues and pull requests](https://github.com/tidyverse/gluedown/issues) to make sure the 24 | bug hasn't been reported and/or already fixed in the development version. By 25 | default, the search will be pre-populated with `is:issue is:open`. You can 26 | [edit the qualifiers](https://help.github.com/articles/searching-issues-and-pull-requests/) 27 | (e.g. `is:pr`, `is:closed`) as needed. For example, you'd simply 28 | remove `is:open` to search _all_ issues in the repo, open or closed. 29 | 30 | 31 | If you _are_ in the right place, and need to file an issue, please review the 32 | ["File issues"](https://www.tidyverse.org/contribute/#issues) paragraph from 33 | the tidyverse contributing guidelines. 34 | 35 | Thanks for your help! 36 | -------------------------------------------------------------------------------- /.github/workflows/check-standard.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check 10 | 11 | jobs: 12 | R-CMD-check: 13 | runs-on: ${{ matrix.config.os }} 14 | 15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | - {os: macos-latest, r: 'release'} 22 | - {os: windows-latest, r: 'release'} 23 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 24 | - {os: ubuntu-latest, r: 'release'} 25 | - {os: ubuntu-latest, r: 'oldrel-1'} 26 | 27 | env: 28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 29 | R_KEEP_PKG_SOURCE: yes 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | 34 | - uses: r-lib/actions/setup-pandoc@v2 35 | 36 | - uses: r-lib/actions/setup-r@v2 37 | with: 38 | r-version: ${{ matrix.config.r }} 39 | http-user-agent: ${{ matrix.config.http-user-agent }} 40 | use-public-rspm: true 41 | 42 | - uses: r-lib/actions/setup-r-dependencies@v2 43 | with: 44 | extra-packages: any::rcmdcheck 45 | needs: check 46 | 47 | - uses: r-lib/actions/check-r-package@v2 48 | with: 49 | upload-snapshots: true 50 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | permissions: 23 | contents: write 24 | steps: 25 | - uses: actions/checkout@v3 26 | 27 | - uses: r-lib/actions/setup-pandoc@v2 28 | 29 | - uses: r-lib/actions/setup-r@v2 30 | with: 31 | use-public-rspm: true 32 | 33 | - uses: r-lib/actions/setup-r-dependencies@v2 34 | with: 35 | extra-packages: any::pkgdown, local::. 36 | needs: website 37 | 38 | - name: Build site 39 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 40 | shell: Rscript {0} 41 | 42 | - name: Deploy to GitHub pages 🚀 43 | if: github.event_name != 'pull_request' 44 | uses: JamesIves/github-pages-deploy-action@v4.4.1 45 | with: 46 | clean: false 47 | branch: gh-pages 48 | folder: docs 49 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage 10 | 11 | jobs: 12 | test-coverage: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - uses: r-lib/actions/setup-r@v2 21 | with: 22 | use-public-rspm: true 23 | 24 | - uses: r-lib/actions/setup-r-dependencies@v2 25 | with: 26 | extra-packages: any::covr 27 | needs: coverage 28 | 29 | - name: Test coverage 30 | run: | 31 | covr::codecov( 32 | quiet = FALSE, 33 | clean = FALSE, 34 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 35 | ) 36 | shell: Rscript {0} 37 | 38 | - name: Show testthat output 39 | if: always() 40 | run: | 41 | ## -------------------------------------------------------------------- 42 | find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true 43 | shell: bash 44 | 45 | - name: Upload test results 46 | if: failure() 47 | uses: actions/upload-artifact@v3 48 | with: 49 | name: coverage-test-failures 50 | path: ${{ runner.temp }}/package 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | inst/doc 5 | docs/ 6 | docs 7 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: gluedown 2 | Title: Wrap Vectors in Markdown Formatting 3 | Version: 1.0.9 4 | Authors@R: 5 | person("Kiernan", "Nicholls", email = "k5cents@gmail.com", role = c("aut", "cre", "cph"), 6 | comment = c(ORCID = "0000-0002-9229-7897")) 7 | Description: Ease the transition between R vectors and markdown text. With 8 | 'gluedown' and 'rmarkdown', users can create traditional vectors in R, 9 | glue those strings together with the markdown syntax, and print those 10 | formatted vectors directly to the document. This package primarily 11 | uses GitHub Flavored Markdown (GFM), an offshoot of the unambiguous 12 | CommonMark specification by John MacFarlane (2019) 13 | . 14 | License: GPL-3 15 | URL: https://k5cents.github.io/gluedown/, https://github.com/k5cents/gluedown/ 16 | BugReports: https://github.com/k5cents/gluedown/issues 17 | Depends: 18 | R (>= 3.3) 19 | Imports: 20 | glue (>= 1.3.1), 21 | magrittr (>= 2.0.1) 22 | Suggests: 23 | covr (>= 3.3.2), 24 | dplyr (>= 0.8.3), 25 | httr (>= 1.4.1), 26 | knitr (>= 1.25), 27 | markdown (>= 1.1), 28 | mockr (>= 0.1), 29 | rmarkdown (>= 1.16), 30 | rvest (>= 0.3.2), 31 | spelling (>= 2.1), 32 | stringr (>= 1.4.0), 33 | testthat (>= 2.1.0), 34 | xml2 (>= 1.3.2) 35 | VignetteBuilder: 36 | knitr 37 | Encoding: UTF-8 38 | Language: en-US 39 | Roxygen: list(markdown = TRUE) 40 | RoxygenNote: 7.2.3 41 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export("%>%") 4 | export(has_knitr) 5 | export(has_markdown) 6 | export(md_autolink) 7 | export(md_blank) 8 | export(md_bold) 9 | export(md_bullet) 10 | export(md_chunk) 11 | export(md_code) 12 | export(md_convert) 13 | export(md_disallow) 14 | export(md_escape) 15 | export(md_fence) 16 | export(md_hardline) 17 | export(md_heading) 18 | export(md_image) 19 | export(md_indent) 20 | export(md_issue) 21 | export(md_italic) 22 | export(md_label) 23 | export(md_link) 24 | export(md_list) 25 | export(md_order) 26 | export(md_paragraph) 27 | export(md_quote) 28 | export(md_reference) 29 | export(md_rule) 30 | export(md_setext) 31 | export(md_softline) 32 | export(md_strike) 33 | export(md_table) 34 | export(md_task) 35 | export(md_text) 36 | importFrom(glue,as_glue) 37 | importFrom(glue,glue) 38 | importFrom(glue,glue_collapse) 39 | importFrom(magrittr,"%>%") 40 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # gluedown 1.0.9 2 | 3 | * Update maintainer email, website URL, and GitHub URL. 4 | 5 | # gluedown 1.0.8 6 | 7 | * Merge pull request #31 from yihui/patch-1 8 | * Updated `md_convert()` for changes in the markdown package. 9 | 10 | # gluedown 1.0.5 11 | 12 | * Update tests for new markdown package functionality (#29). 13 | 14 | # gluedown 1.0.4 15 | 16 | * Remove `LazyData` from DESCRIPTION per new CRAN rules. Package has no data. 17 | * The vignettes are more careful when fetching resources from the internet. 18 | 19 | # gluedown 1.0.3 20 | 21 | * Update `md_table()` tests for rvest 1.0.0 release. 22 | * Create `md_label()` to use with `md_reference()` (#25). 23 | * Try using clean `...` support in `md_link()` and `md_image()` (#26). 24 | * Use `goodpractices::gp()` to fix a few issues: 25 | 1. Trim all lines to under 80 character width. 26 | 2. Use `vapply()` over `sapply()` in `md_setext()`. 27 | * The `md_link()` and `md_image()` functions have half-baked support for using 28 | named `...` arguments instead of the traditional vector arguments. This works 29 | well with a single link but not when trying to turn two lengthy vectors into 30 | multiple markdown links (#26). 31 | 32 | # gluedown 1.0.2 33 | 34 | * Suggest `markdown` dependency and warn for `md_convert()` (#22). 35 | * Suggest `knitr` dependency and write fallback for `md_table()` (#20). 36 | * Enable named vector support for `md_link()` (#26). 37 | 38 | # gluedown 1.0.1 39 | 40 | * Remove `md_define()` to avoid pandoc issue (#16, #17). 41 | * Remove all `stringr` functions in place of `base` alternatives (#15). 42 | * Remove `map_md()` function. 43 | * Remove `dplyr` suggestion dependency. 44 | * Remove `httr` tests in `md_issue()`. 45 | * Remove `httr` suggestion dependency. 46 | * Improve vignettes. 47 | 48 | # gluedown 1.0.0 49 | 50 | * Remove `readr::read_lines()` from docs. 51 | * Cite CommonMark author in `DESCRIPTION`. 52 | * Remove the `%>%` utility and magrittr package. 53 | * Remove all `/docs` and move to `gh-pages` branch. 54 | * Re-submit to CRAN. 55 | 56 | # gluedown 0.4.4 57 | 58 | * Replace `emphasize_at()` with `map_md()`, which now uses 59 | `str_split(simplify = TRUE)` and `apply()` instead of `purrr::map_at()`. 60 | 61 | # gluedown 0.4.3 62 | 63 | * Add vignette walking through all the features of GFM. 64 | 65 | # gluedown 0.4.2 66 | 67 | * Create `md_disallow()` and add to `md_convert()` (#10). 68 | * Create `md_text()` wrapper for `glue::as_glue()` (#10). 69 | * Create `md_blank()` (#10). 70 | 71 | # gluedown 0.4.1 72 | 73 | * Cover all errors in `md_rule()` and `md_chunk()`, brings coverage to 100%. 74 | 75 | # gluedown 0.4.0 76 | 77 | * Add back all the tests from the GFM spec (#13). 78 | * Create `md_autolink()` (#10). 79 | 80 | # gluedown 0.3.3 81 | 82 | * Add and remove tests from the GFM spec (#13). 83 | 84 | # gluedown 0.3.2 85 | 86 | * Use `...` in `md_hardline()`, `md_softline()`, and `md_paragraph()`. 87 | 88 | # gluedown 0.3.1 89 | 90 | * Rename `md-break.R` to `md-rule.R` 91 | * Implement optional spacing in `md_rule()` 92 | * Implement optional custom underline width in `md_setext()`. 93 | 94 | # gluedown 0.3.0 95 | 96 | * Create `md_paragraph()` and `md_softline()` (#10). 97 | 98 | # gluedown 0.2.3 99 | 100 | * Rewrite function descriptions with consistent language. 101 | 102 | # gluedown 0.2.2 103 | 104 | * Check if `any()` number in `md_issue()` can't be coerced to numeric. 105 | 106 | # gluedown 0.2.1 107 | 108 | * Return glued `md_convert()` output with `glue::as_glue()`. 109 | * Return glued `emphasize_at()` output. 110 | * Add description of `kable` and `markdown` wrapping in `gluedown.R`. 111 | 112 | # gluedown 0.2.0 113 | 114 | * Update the documentation for block container functions. 115 | * Update the documentation for inline functions. 116 | * Rename `md_list()` to `md_order()`. 117 | * Create generic `md_list()` to call all list types. 118 | * Implement `usethis::use_spell_check()`. 119 | * Improve template GitHub issue. 120 | * Create and delete `md_emoji()` (want the reverse, maybe). 121 | 122 | # gluedown 0.1.4 123 | 124 | * Split `md_chunk()` into `md_indent()` and `md_fence()`. 125 | * Use `glue::glue_collapse()` in `md_rule()` and `md_table()`. 126 | * Use `stringr::str_dup()` in `md_heading()`, `md_setext()`, and `md_rule()`. 127 | * Update unit tests for the new versions of leaf block functions. 128 | * Update the documentation for leaf block functions. 129 | 130 | # gluedown 0.1.2 131 | 132 | * Create `md_hardline()` (#10). 133 | 134 | # gluedown 0.1.1 135 | 136 | * Create `md_setext()` (#10). 137 | * Add family documentation (#11). 138 | 139 | # gluedown 0.1.0 140 | 141 | * Added a `NEWS.md` file to track changes to the package. 142 | * Use `glue::glue()` everywhere over `paste()` w/ `cat()` (#8). 143 | * Fully test every function argument (_coverage: 97%_) (#9). 144 | -------------------------------------------------------------------------------- /R/gluedown-package.R: -------------------------------------------------------------------------------- 1 | #' @title gluedown: A package to format character vectors with markdown. 2 | #' 3 | #' @description The `gluedown` package helps transition from R's powerful 4 | #' vectors to formatted markdown text. The functions use [glue::glue()] to 5 | #' wrap character vectors in valid markdown syntax. In combination with the 6 | #' `knitr` package, this allows users to directly print R vectors as formatted 7 | #' text for improved clarity and readability. 8 | #' 9 | #' @section Glue wrappers: 10 | #' The `md_*()` functions return `glue` objects, which are returned using 11 | #' [cat()] by default. This allows users to both manipulate the formatted 12 | #' strings as they would with any character vector and still present the 13 | #' string to the user when an `knitr` chunk option is set to return code 14 | #' results `'asis'`. 15 | #' 16 | #' @section Other wrappers: 17 | #' The [md_table()] and [md_convert()] functions wrap around [knitr::kable()] 18 | #' and [markdown::markdownToHTML()] respectively. The later allows users to 19 | #' convert `md_*()` outputs to HTML fragments. 20 | #' 21 | #' @keywords internal 22 | "_PACKAGE" 23 | 24 | ## usethis namespace: start 25 | ## usethis namespace: end 26 | NULL 27 | -------------------------------------------------------------------------------- /R/md-autolink.R: -------------------------------------------------------------------------------- 1 | #' Markdown autolink 2 | #' 3 | #' Take a character vector and wrap each element in `<` and `>` to return a glue 4 | #' vector of autolink text. This inline is rendered as the `` HTML tag. 5 | #' 6 | #' @details 7 | #' Autolinks are absolute URIs and email addresses inside `<` and `>`. They are 8 | #' parsed as links, with the URL or email address as the link label. 9 | #' 10 | #' A URI autolink consists of `<`, followed by an absolute URI followed by `>`. 11 | #' It is parsed as a link to the URI, with the URI as the link’s label. 12 | #' 13 | #' An absolute URI, for these purposes, consists of a scheme followed by a colon 14 | #' (`:`) followed by zero or more characters other than ASCII whitespace and 15 | #' control characters, `<`, and `>`. If the URI includes these characters, they 16 | #' must be percent-encoded (e.g. `%20` for a space). 17 | #' 18 | #' For purposes of this spec, a scheme is any sequence of 2–32 characters 19 | #' beginning with an ASCII letter and followed by any combination of ASCII 20 | #' letters, digits, or the symbols plus (”+”), period (”.”), or hyphen (”-”). 21 | #' @param url A character vector of absolute URLs. 22 | #' @return A `glue` vector of length equal to `x`. 23 | #' @family inline functions 24 | #' @examples 25 | #' md_autolink("http://foo.bar.baz") 26 | #' @importFrom glue glue 27 | #' @export 28 | md_autolink <- function(url) { 29 | glue::glue("<{url}>") 30 | } 31 | -------------------------------------------------------------------------------- /R/md-chunk.R: -------------------------------------------------------------------------------- 1 | #' Markdown code block 2 | #' 3 | #' Take a character vector of lines and return a glue vector 4 | #' 5 | #' Turn a character vector of lines into a single code block either indented or 6 | #' fenced in tildes or backticks. This markdown leaf block can be rendered as 7 | #' nested HTML `` 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 | #' * `<textarea>`
47 | #' * `<style>`
48 | #' * `<xmp>`
49 | #' * `<iframe>`
50 | #' * `<noembed>`
51 | #' * `<noframes>`
52 | #' * `<script>`
53 | #' * `<plaintext>`
54 | #'
55 | #' Filtering is done by replacing the leading `<` with the entity `<`. These
56 | #' tags are chosen in particular as they change how HTML is interpreted in a way
57 | #' unique to them (i.e. nested HTML is interpreted differently), and this is
58 | #' usually undesireable (sic) in the context of other rendered Markdown content.
59 | #'
60 | #' All other HTML tags are left untouched.
61 | #' @param html A character vector of _markdown_ text to be converted.
62 | #' @return A `glue` vector of length 1 containing HTML tags.
63 | #' @family inline functions
64 | #' @examples
65 | #' md_disallow("<title>GitHub Flavored Markdown Spec")
66 | #' @importFrom glue as_glue
67 | #' @export
68 | md_disallow <- function(html) {
69 |   bad <- c(
70 |     "<(title)",
71 |     "<(textarea)",
72 |     "<(style)",
73 |     "<(xmp)",
74 |     "<(iframe)",
75 |     "<(noembed)",
76 |     "<(noframes)",
77 |     "<(script)",
78 |     "<(plaintext)"
79 |   )
80 |   for (i in seq_along(bad)) {
81 |     html <- gsub(
82 |       pattern = bad[[i]],
83 |       replacement = "<\\1",
84 |       x = html,
85 |       ignore.case = TRUE
86 |     )
87 |   }
88 |   glue::as_glue(html)
89 | }
90 | 


--------------------------------------------------------------------------------
/R/md-escape.R:
--------------------------------------------------------------------------------
 1 | #' Backslash escape all punctuation
 2 | #'
 3 | #' Take a character vector containing punctuation and return a glue vector with
 4 | #' every punctuation mark prepended with double escape backslashes.
 5 | #'
 6 | #' When trying to format text containing markdown syntax characters, it's
 7 | #' necessary to "escape" those characters so that they are ignored by
 8 | #' formatting.
 9 | #'
10 | #' @details
11 | #' Any ASCII punctuation character may be backslash-escaped... Escaped
12 | #' characters are treated as regular characters and do not have their usual
13 | #' Markdown meanings.
14 | #' @param x A character vector of strings containing punctuation that might
15 | #'   accidentally be considered markdown syntax.
16 | #' @return A character string with all `[:punct:]` properly escaped with
17 | #'   prepended backslashes.
18 | #' @family inline functions
19 | #' @examples
20 | #' md_escape("# six seasons and a movie")
21 | #' @importFrom glue as_glue
22 | #' @export
23 | md_escape <- function(x) {
24 |   glue::as_glue(gsub(pattern = "([[:punct:]])", replacement = "\\\\\\1", x))
25 | }
26 | 


--------------------------------------------------------------------------------
/R/md-heading.R:
--------------------------------------------------------------------------------
  1 | #' Markdown ATX headings
  2 | #'
  3 | #' Turn a character vector into a vector of valid markdown ATX headings. These
  4 | #' markdown leaf blocks can be rendered as the `

` through `

` HTML tags. 5 | #' See [md_setext()] to create setext (underlined) headings. 6 | #' 7 | #' @details 8 | #' An ATX heading consists of a string of characters, parsed as inline content, 9 | #' between an opening sequence of 1–6 unescaped `#` characters and an optional 10 | #' closing sequence of any number of unescaped `#` characters. The opening 11 | #' sequence of `#` characters must be followed by a space or by the end of line. 12 | #' The optional closing sequence of `#`s must be preceded by a space and may be 13 | #' followed by spaces only. The opening # character may be indented 0-3 spaces. 14 | #' The raw contents of the heading are stripped of leading and trailing spaces 15 | #' before being parsed as inline content. The heading level is equal to the 16 | #' number of `#` characters in the opening sequence. 17 | #' @param x A character vector of heading text. 18 | #' @param level A numeric vector of use to determine the number of heading hash 19 | #' characters to preceed each element of `x`. The heading level is equal to 20 | #' the number of `#` characters in the opening sequence. 21 | #' @return A `glue` vector of headings with length equal to `x`. 22 | #' @family leaf block functions 23 | #' @examples 24 | #' md_heading("Overview") 25 | #' md_heading(x = c("One", "Two"), level = 1:2) 26 | #' md_heading(x = c("Installation", "Usage"), level = 2) 27 | #' @importFrom glue glue 28 | #' @export 29 | md_heading <- function(x, level = 1) { 30 | if (max(level) > 6 | min(level) < 1) { 31 | stop("ATX headings only support levels 1 through 6.") 32 | } 33 | glue::glue("{strrep('#', level)} {x}") 34 | } 35 | 36 | #' Markdown Setext headings (4.3) 37 | #' 38 | #' Turn a character vector into a vector of valid markdown Setext headings. 39 | #' These markdown leaf blocks can be rendered as the `

` and `

` tags 40 | #' _only_. 41 | #' 42 | #' @details 43 | #' A setext heading consists of one or more lines of text, each containing at 44 | #' least one non-whitespace character, with no more than 3 spaces indentation, 45 | #' followed by a setext heading underline. The lines of text must be such that, 46 | #' were they not followed by the setext heading underline, they would be 47 | #' interpreted as a paragraph: they cannot be interpretable as a 48 | #' [code fence](https://github.github.com/gfm/#code-fence), 49 | #' [ATX heading](https://github.github.com/gfm/#atx-headings), 50 | #' [block quote](https://github.github.com/gfm/#block-quotes), 51 | #' [thematic break](https://github.github.com/gfm/#thematic-breaks), 52 | #' [list item](https://github.github.com/gfm/#list-items), or 53 | #' [HTML block.](https://github.github.com/gfm/#html-blocks) 54 | #' 55 | #' A setext heading underline is a sequence of `=` characters or a sequence of 56 | #' `-` characters, with no more than 3 spaces indentation and any number of 57 | #' trailing spaces. If a line containing a single `-` can be interpreted as an 58 | #' empty list items, it should be interpreted this way and not as a setext 59 | #' heading underline. 60 | #' 61 | #' The heading is a level 1 heading if `=` characters are used in the setext 62 | #' heading underline, and a level 2 heading if `-` characters are used. The 63 | #' contents of the heading are the result of parsing the preceding lines of text 64 | #' as CommonMark inline content. 65 | #' 66 | #' In general, a setext heading need not be preceded or followed by a blank 67 | #' line. However, it cannot interrupt a paragraph, so when a setext heading 68 | #' comes after a paragraph, a blank line is needed between them. 69 | #' @param x A character vector of heading text. 70 | #' @param level An numeric vector of all either 1 or 2 to determine whether 71 | #' level 1 headings are created with `=` or level two with `-`. If less levels 72 | #' are provided than headings, `level` will be repeated via [glue::glue()]. 73 | #' @param width logical or integer; if `TRUE` the width will be automatically 74 | #' determined by the width of the longest line in `x`. If an integer, the 75 | #' setext underline will be that wide. 76 | #' @return A `glue` vector of headings with length equal to `x`. 77 | #' @importFrom glue glue glue_collapse 78 | #' @family leaf block functions 79 | #' @examples 80 | #' md_setext("Overview") 81 | #' md_setext("This is a setext\nheading", level = 2) 82 | #' md_setext(c("one", "two", "three", "four"), level = c(1, 2)) 83 | #' md_setext("Installation", level = 2, width = 55) 84 | #' @export 85 | md_setext <- function(x, level = 1, width = TRUE) { 86 | if (any(x == "")) { 87 | stop("Setext headings cannot be empty.") 88 | } 89 | char <- if (max(level) > 2 | min(level) < 1) { 90 | stop("Setext headings only support levels 1 and 2.") 91 | } else { 92 | char <- c("=", "-")[level] 93 | } 94 | n <- if ((all(is.logical(width)) & isTRUE(width)) | min(width) < 1) { 95 | vapply(strsplit(x, "\n"), function(y) max(nchar(y)), FUN.VALUE = integer(1)) 96 | } else { 97 | width 98 | } 99 | glue::glue("{x}\n{strrep(char, n)}") 100 | } 101 | -------------------------------------------------------------------------------- /R/md-inlines.R: -------------------------------------------------------------------------------- 1 | #' Markdown code span 2 | #' 3 | #' Take a character vector and wrap each element in backticks to create a glue 4 | #' vector of inline code spans. This inline is rendered as a `` HTML tag. 5 | #' 6 | #' @details 7 | #' A backtick string is a string of one or more backtick characters that is 8 | #' neither preceded nor followed by a backtick. 9 | #' 10 | #' A code span begins with a backtick string and ends with a backtick string 11 | #' of equal length. The contents of the code span are the characters between 12 | #' the two backtick strings, normalized in the following ways: * First, line 13 | #' endings are converted to spaces. * If the resulting string both begins and 14 | #' ends with a space character, but does not consist entirely of space 15 | #' characters, a single space character is removed from the front and back. 16 | #' This allows you to include code that begins or ends with backtick 17 | #' characters, which must be separated by whitespace from the opening or 18 | #' closing backtick strings. 19 | #' @param x The text to be formatted as fixed-width inline code. 20 | #' @return A `glue` vector of length equal to `x`. 21 | #' @family inline functions 22 | #' @examples 23 | #' md_code("ex_var") 24 | #' md_code(state.name[1:3]) 25 | #' @importFrom glue glue 26 | #' @export 27 | md_code <- function(x) { 28 | if (any(grepl("`", x))) { 29 | glue::glue("`` {x} ``") 30 | } else { 31 | glue::glue("`{x}`") 32 | } 33 | } 34 | 35 | #' Markdown bold emphasis 36 | #' 37 | #' Take a character vector and wrap each element in double asterisks to create a 38 | #' glue vector of bold emphasis text. This inline is rendered as the `` 39 | #' HTML tag. 40 | #' 41 | #' @details 42 | #' A double `**` or `__` can open or close emphasis... Emphasis begins with a 43 | #' delimiter that can open emphasis and ends with a delimiter that can close 44 | #' emphasis, and that uses the same character (`__` or `**`) as the opening 45 | #' delimiter. 46 | #' @param x The text to be emphasized in bold. 47 | #' @return A `glue` vector of length equal to `x`. 48 | #' @family inline functions 49 | #' @examples 50 | #' md_bold("Example") 51 | #' md_bold(state.name) 52 | #' @importFrom glue glue 53 | #' @export 54 | md_bold <- function(x) { 55 | glue::glue("**{x}**") 56 | } 57 | 58 | #' Markdown italic emphasis 59 | #' 60 | #' Take a character vector and wrap each element in single underscores to create 61 | #' a glue vector of italic emphasis text. This inline is rendered as the `` 62 | #' HTML tag. 63 | #' 64 | #' @details 65 | #' A single `*` or `_` can open or close emphasis... Emphasis begins with a 66 | #' delimiter that can open emphasis and ends with a delimiter that can close 67 | #' emphasis, and that uses the same character (`_` or `*`) as the opening 68 | #' delimiter. 69 | #' @param x The text to be emphasized in italics. 70 | #' @return A `glue` vector of length equal to `x`. 71 | #' @family inline functions 72 | #' @examples 73 | #' md_italic("Example") 74 | #' md_italic(state.name) 75 | #' @importFrom glue glue 76 | #' @export 77 | md_italic <- function(x) { 78 | glue::glue("_{x}_") 79 | } 80 | 81 | #' Markdown strikethrough (extension) 82 | #' 83 | #' Take a character vector and wrap each element in tildes to create a glue 84 | #' vector of strikethrough text. This inline is rendered as the `` HTML 85 | #' tag. 86 | #' 87 | #' @details 88 | #' GFM enables the strikethrough extension, where an additional emphasis type is 89 | #' available. Strikethrough text is any text wrapped in two tildes (`~`). 90 | #' @param x A character vector of text to be striked through. 91 | #' @return A `glue` vector of length equal to `x`. 92 | #' @family inline functions 93 | #' @family markdown extensions 94 | #' @examples 95 | #' md_strike("Example") 96 | #' md_strike(state.name[1:3]) 97 | #' @importFrom glue glue 98 | #' @export 99 | md_strike <- function(x) { 100 | glue::glue("~~{x}~~") 101 | } 102 | -------------------------------------------------------------------------------- /R/md-issue.R: -------------------------------------------------------------------------------- 1 | #' Markdown GitHub issue 2 | #' 3 | #' Take a character vector and numeric vector and concatenate them into a glue 4 | #' vector of valid GitHub issue autolinks (username/repo#issue). 5 | #' 6 | #' @details 7 | #' Within conversations on GitHub, references to issues and pull requests are 8 | #' [automatically converted to shortened links](https://git.io/Jvtb9). 9 | #' @param repo A character vector in the format `"user/rep"`. 10 | #' @param num The issue or pull number _without_ hash symbol. 11 | #' @return A character vector which GitHub can automatically hyperlink. 12 | #' @family inline functions 13 | #' @family markdown extensions 14 | #' @examples 15 | #' md_issue("k5cents/gluedown", 1:5) 16 | #' @export 17 | md_issue <- function(repo, num) { 18 | if (suppressWarnings(any(is.na(as.numeric(num))))) { 19 | stop("The num must be coercible to numeric.") 20 | } 21 | if (!grepl("/", repo)) { 22 | warning("use the \"user/repo\" format") 23 | } 24 | glue::glue("{repo}#{num}") 25 | } 26 | -------------------------------------------------------------------------------- /R/md-lines.R: -------------------------------------------------------------------------------- 1 | #' Markdown soft line breaks 2 | #' 3 | #' Take a character vector and return a glue vector of separated by a single 4 | #' newline. This inline is rendered as single `

` HTML tags. 5 | #' 6 | #' @details 7 | #' A regular line break (not in a code span or HTML tag) that is not preceded by 8 | #' two or more spaces or a backslash is parsed as a softbreak. (A softbreak may 9 | #' be rendered in HTML either as a line ending or as a space. The result will be 10 | #' the same in browsers. In the examples here, a line ending will be used.) 11 | #' @param ... Any number of character vectors. 12 | #' @return A `glue` vector with elements of `...` separated by a single newline. 13 | #' @family inline functions 14 | #' @examples 15 | #' # compare the following 16 | #' md_bold(c("One", "Two")) 17 | #' 18 | #' md_softline(md_bold(c("One", "Two"))) 19 | #' @importFrom glue glue_collapse 20 | #' @export 21 | md_softline <- function(...) { 22 | dots <- unlist(list(...)) 23 | glue::glue("{dots}\n") 24 | } 25 | 26 | #' Markdown hard line breaks 27 | #' 28 | #' Take a character vector and return a collapsed glue vector with each original 29 | #' element separated by two spaces and a newline. This inline is rendered with a 30 | #' `
` HTML tag. 31 | #' 32 | #' @details 33 | #' A line break (not in a code span or HTML tag) that is preceded by two or more 34 | #' spaces and does not occur at the end of a block is parsed as a hard line 35 | #' break (rendered in HTML as a `
` tag) 36 | #' @param ... Any number of character vectors. 37 | #' @return A `glue` vector with elements of `...` separated by two trailing 38 | #' spaces and a single newline. 39 | #' @family inline functions 40 | #' @examples 41 | #' # compare the following 42 | #' md_bold(c("One", "Two")) 43 | #' md_hardline(md_bold(c("One", "Two")), md_italic("Three")) 44 | #' @importFrom glue glue_collapse 45 | #' @export 46 | md_hardline <- function(...) { 47 | dots <- unlist(list(...)) 48 | glue::glue("{dots} \n") 49 | } 50 | 51 | #' Markdown paragraphs breaks 52 | #' 53 | #' Take a character vector and return a glue vector of paragraphs separated by 54 | #' double newlines. This leaf block is rendered as distinct `

` HTML tags. 55 | #' 56 | #' @details 57 | #' A sequence of non-blank lines that cannot be interpreted as other kinds of 58 | #' blocks forms a paragraph. The contents of the paragraph are the result of 59 | #' parsing the paragraph’s raw content as inlines. The paragraph’s raw content 60 | #' is formed by concatenating the lines and removing initial and final 61 | #' whitespace... Paragraphs can contain multiple lines, but no blank lines. 62 | #' @param ... Any number of character vectors. 63 | #' @return A `glue` vector with elements of `...` separated by two newlines. 64 | #' @family leaf block functions 65 | #' @examples 66 | #' md_paragraph(stringr::sentences[1:3]) 67 | #' @importFrom glue glue 68 | #' @export 69 | md_paragraph <- function(...) { 70 | dots <- unlist(list(...)) 71 | glue::glue("{dots}\n\n") 72 | } 73 | 74 | #' Markdown blank line 75 | #' 76 | #' Create a blank line between other markdown block-level elements. 77 | #' 78 | #' @details 79 | #' Blank lines between block-level elements are ignored, except for the role 80 | #' they play in determining whether a list is tight or loose. 81 | #' 82 | #' Blank lines at the beginning and end of the document are also ignored. 83 | #' @return A `glue` vector of length one containing two newline characters. 84 | #' @family leaf block functions 85 | #' @examples 86 | #' md_blank 87 | #' @importFrom glue glue 88 | #' @export 89 | md_blank <- function() { 90 | glue::glue("\n") 91 | } 92 | -------------------------------------------------------------------------------- /R/md-link.R: -------------------------------------------------------------------------------- 1 | #' Markdown inline link (6.6) 2 | #' 3 | #' Take character vectors of link texts, link destinations, and optional titles 4 | #' and return single glue vector of valid markdown inline links. This inline is 5 | #' rendered as the `` HTML tag. 6 | #' 7 | #' @details 8 | #' A link contains link text (the visible text), a link destination (the URI 9 | #' that is the link destination), and optionally a link title. There are two 10 | #' basic kinds of links in Markdown. In inline links the destination and title 11 | #' are given immediately after the link text. 12 | #' 13 | #' A link text consists of a sequence of zero or more inline elements enclosed 14 | #' by square brackets (`[` and `]`)... 15 | #' 16 | #' An inline link consists of a link text followed immediately by a left 17 | #' parenthesis `(`, optional whitespace, an optional link destination, an 18 | #' optional link title separated from the link destination by whitespace, 19 | #' optional whitespace, and a right parenthesis `)`. The link’s text consists of 20 | #' the inlines contained in the link text (excluding the enclosing square 21 | #' brackets). The link’s URI consists of the link destination, excluding 22 | #' enclosing `<...>` if present, with backslash-escapes in effect as described 23 | #' above. The link’s title consists of the link title, excluding its enclosing 24 | #' delimiters, with backslash-escapes in effect as described above. 25 | #' @param text A character vector of text with another vector of URLs passed to 26 | #' the `url` argument. 27 | #' @param url A character vector of URLs. 28 | #' @param title The optional title of the link. 29 | #' @param ... A sequence of `text = "/url"` named vector pairs. If any such 30 | #' pairs are provided, `.name` will be considered `TRUE`. 31 | #' @param .name logical; if `TRUE`, the pairs in `...` will be used instead of 32 | #' any values supplied to `x` and `url`. 33 | #' @return A `glue` vector of collapsed display text and associated URLs. 34 | #' @family inline functions 35 | #' @examples 36 | #' md_link(1:5, glue::glue("https://{state.abb[1:5]}.gov"), state.name[1:5]) 37 | #' md_link(CRAN = "https://cran.r-project.org/") 38 | #' @importFrom glue glue 39 | #' @export 40 | md_link <- function(text, url, title = NULL, ..., .name = FALSE) { 41 | x <- unlist(list(...)) 42 | if (!is.null(x) | .name) { 43 | glue::glue("[{names(x)}]({unlist(x)})") 44 | } else if (!is.null(title)) { 45 | glue::glue("[{text}]({url} \"{title}\")") 46 | } else { 47 | glue::glue("[{text}]({url})") 48 | } 49 | } 50 | 51 | 52 | #' Markdown image links 53 | #' 54 | #' Take character vectors of alternative text, image link destinations, and 55 | #' optional titles and return single glue vector of valid markdown inline image 56 | #' links. This inline is rendered as the `` HTML tag. Note that the 57 | #' expected arguments of `md_image()` are reversed from `md_link()` 58 | #' 59 | #' @details 60 | #' Syntax for images is like the syntax for links, with one difference. Instead 61 | #' of link text, we have an image description. The rules for this are the same 62 | #' as for link text, except that (a) an image description starts with `![` 63 | #' rather than `[`, and (b) an image description may contain links. An image 64 | #' description has inline elements as its contents. When an image is rendered to 65 | #' HTML, this is standardly used as the image’s `alt` attribute. 66 | #' @param url A character vector of link destination (URL) strings. 67 | #' @param alt A character vector of alternative text that can be used to refer 68 | #' to an image. 69 | #' @param title The optional title of the link. 70 | #' @param ... A sequence of `alt = "/url"` named vector pairs. If any such 71 | #' pairs are provided, `.name` will be considered `TRUE`. 72 | #' @param .name logical; if `TRUE`, the pairs in `...` will be used instead of 73 | #' any values supplied to `x` and `url`. 74 | #' @return A `glue` vector of collapsed display text and associated URLs. 75 | #' @return A `glue` vector of collapsed alternative text and associated URLs. 76 | #' @family inline functions 77 | #' @examples 78 | #' if (file.exists("man/figures/logo.png")) md_image("man/figures/logo.png") 79 | #' md_image("http://hexb.in/hexagons/eff.png") 80 | #' md_image(EFF = "http://hexb.in/hexagons/eff.png") 81 | #' md_image("http://hexb.in/hexagons/eff.png", "EFF Hex Sticker", "Logo") 82 | #' @importFrom glue glue 83 | #' @export 84 | md_image <- function(url, alt = "", title = NULL, ..., .name = FALSE) { 85 | x <- unlist(list(...)) 86 | if (!is.null(x) | .name) { 87 | if (is.null(names(x))) { 88 | glue::glue("![]({unlist(x)})") 89 | } else { 90 | glue::glue("![{names(x)}]({unlist(x)})") 91 | } 92 | } else if (!is.null(title)) { 93 | glue::glue("![{alt}]({url} \"{title}\")") 94 | } else { 95 | glue::glue("![{alt}]({url})") 96 | } 97 | } 98 | 99 | #' Markdown link label 100 | #' 101 | #' Create the link labels that can latter be referred to with a link reference 102 | #' from [md_reference()]. 103 | #' 104 | #' @details 105 | #' A link label begins with a left bracket and ends with the first right bracket 106 | #' that is not backslash-escaped. Between these brackets there must be at least 107 | #' one non-whitespace character. 108 | #' @param text The text in the document to be hyperlinked. 109 | #' @param label A link label that is referenced elsewhere in the document. 110 | #' @param ... A sequence of `label = "text"` named vector pairs. If any such 111 | #' pairs are provided, `.name` will be considered `TRUE`. 112 | #' @param .name logical; if `TRUE`, the pairs in `...` will be used instead of 113 | #' any values supplied to `x` and `url`. 114 | #' @return A single `glue` vector of length equal to that of `label` and `url`, 115 | #' with elements the concatenated arguments. 116 | #' @family leaf block functions 117 | #' @examples 118 | #' md_label(CRAN = "The CRAN website") 119 | #' md_label(text = c("one", "two"), label = 1:2) 120 | #' @export 121 | md_label <- function(text, label, ..., .name = FALSE) { 122 | x <- unlist(list(...)) 123 | if (!is.null(x) | .name) { 124 | glue::glue("[{unlist(x)}][{names(x)}]") 125 | } else { 126 | glue::glue("[{text}][{label}]") 127 | } 128 | } 129 | 130 | #' Markdown link reference 131 | #' 132 | #' Take character vectors of link texts, link destinations, and optional titles 133 | #' and return single glue vector of valid markdown link references. This 134 | #' markdown leaf block then uses the `label` placed _elsewhere_ in a markdown 135 | #' document to render `` HTML tags. 136 | #' 137 | #' @details 138 | #' A full reference link (6.6) consists of a link text immediately followed by a 139 | #' link label that matches a link reference definition elsewhere in the 140 | #' document... 141 | #' 142 | #' A link reference definition consists of a link label, indented up to three 143 | #' spaces, followed by a colon (`:`), optional whitespace (including up to one 144 | #' line ending), a link destination, optional whitespace (including up to one 145 | #' line ending), and an optional link title, which if it is present must be 146 | #' separated from the link destination by whitespace. No further non-whitespace 147 | #' characters may occur on the line. 148 | #' 149 | #' A link reference definition does not correspond to a structural element of a 150 | #' document. Instead, it defines a label which can be used in reference links 151 | #' and reference-style images elsewhere in the document. Link reference 152 | #' definitions can come either before or after the links that use them. 153 | #' @param label A link label that is referenced elsewhere in the document. 154 | #' @param url The URL to hyperlink the referenced text with. 155 | #' @param title An _optional_ link title; defaults to `NULL`. 156 | #' @param ... A sequence of `label = "/url"` named vector pairs. If any such 157 | #' pairs are provided, `.name` will be considered `TRUE`. 158 | #' @param .name logical; if `TRUE`, the pairs in `...` will be used instead of 159 | #' any values supplied to `x` and `url`. 160 | #' @return A single `glue` vector of length equal to that of `label` and `url`, 161 | #' with elements the concatenated arguments. 162 | #' @family leaf block functions 163 | #' @examples 164 | #' md_reference(CRAN = "https://cran.r-project.org/") 165 | #' md_reference(label = 1:2, url = c("https://one.org", "https://two.com")) 166 | #' md_reference("tv", "https://www.tidyverse.org/", title = "tidyverse") 167 | #' @export 168 | md_reference <- function(label, url, title = NULL, ..., .name = FALSE) { 169 | x <- unlist(list(...)) 170 | if (!is.null(x) | .name) { 171 | glue::glue("[{names(x)}]: {unlist(x)}") 172 | } else if (!is.null(title)) { 173 | glue::glue("[{label}]: {url} \"{title}\"") 174 | } else { 175 | glue::glue("[{label}]: {url}") 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /R/md-list.R: -------------------------------------------------------------------------------- 1 | #' Markdown generic list 2 | #' 3 | #' Turn a character vector into a valid markdown list block. This is a generic 4 | #' function that calls [md_bullet()], [md_order()], or [md_task()] depending on 5 | #' what string is provided in the `type` argument. 6 | #' 7 | #' @param x A character vector of list items. 8 | #' @param type The type of list to create; either bullet, ordered, or task. 9 | #' @param ... Arguments passed to the appropriate list type function. 10 | #' @return A `glue` vector with length equal to `x`. 11 | #' @family container block functions 12 | #' @examples 13 | #' md_list(state.name[1:5], type = "bullet", marker = "+") 14 | #' md_list(state.name[6:10], type = "ordered", marker = ")") 15 | #' md_list(state.name[11:15], type = "task", check = 3:5) 16 | #' @export 17 | md_list <- function(x, type = c("bullet", "ordered", "task"), ...) { 18 | type <- match.arg(type) 19 | if (type == "bullet") { 20 | md_bullet(x, ...) 21 | } else { 22 | if (type == "ordered") { 23 | md_order(x, ...) 24 | } else { 25 | md_task(x, ...) 26 | } 27 | } 28 | } 29 | 30 | #' Markdown bullet list 31 | #' 32 | #' take a character vector and return a glue vector of valid bullet list items. 33 | #' When printed together, these bullet list items create a bullet list. This 34 | #' container block is rendered as the `