diff --git a/NAMESPACE b/NAMESPACE index 78d0a37..15fa0cc 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,6 +2,7 @@ S3method(print,summary_spellcheck) export(get_wordlist) +export(spell_check_bookdown) export(spell_check_files) export(spell_check_package) export(spell_check_setup) diff --git a/R/spell-check-bookdown.R b/R/spell-check-bookdown.R new file mode 100644 index 0000000..3f47a45 --- /dev/null +++ b/R/spell-check-bookdown.R @@ -0,0 +1,103 @@ +#' Bookdown Spell Checking +#' +#' Automatically spell-check bookdown source files. +#' +#' Parses and checks bookdown source files, and if these files are present +#' * README.md (when there's no README.Rmd) +#' * NEWS.md +#' * text fields in the `DESCRIPTION` file if present. +#' +#' The preferred spelling language (typically `en-GB` or `en-US`) should be +#' * specified in the `Language` field from your package `DESCRIPTION`, +#' * or entered as the `lang` argument. +#' To whitelist custom words +#' use the bookdown [WORDLIST][get_wordlist] file which will be added to the dictionary +#' when spell checking. See [update_wordlist] to automatically populate and update this +#' file. +#' +#' Hunspell includes dictionaries for `en_US` and `en_GB` by default. Other languages +#' require installation of a custom dictionary, see [hunspell][hunspell::hunspell] for details. +#' +#' @export +#' @rdname spell_check_bookdown +#' @name spell_check_bookdown +#' @aliases spelling +#' @family spelling +#' @param path path to package root directory containing the `DESCRIPTION` file +#' @param use_wordlist ignore words in the package [WORDLIST][get_wordlist] file +#' @param lang set `Language` field in `DESCRIPTION` e.g. `"en-US"` or `"en-GB"`. +#' For supporting other languages, see the [hunspell vignette](https://docs.ropensci.org/hunspell/articles/intro.html#hunspell-dictionaries). +spell_check_bookdown <- function(path = ".", lang = NULL, use_wordlist = TRUE){ + + # Get language + if (is.null(lang)) { + if (file.exists(file.path(path, "DESCRIPTION"))){ + pkg <- as_package(path) + + # Get language from DESCRIPTION + lang <- normalize_lang(pkg$language) + } else { + lang <- suppressMessages(normalize_lang(NULL)) + } + } else { + lang <- normalize_lang(lang) + } + + # Add custom words to the ignore list + + add_words <- if(isTRUE(use_wordlist)) + get_wordfile(path) + + if (file.exists(file.path(path, "DESCRIPTION"))){ + pkg <- as_package(path) + author <- if(length(pkg[['authors@r']])){ + parse_r_field(pkg[['authors@r']]) + } else { + if ("author" %in% names(pkg)) { + strsplit(pkg[['author']], " ", fixed = TRUE)[[1]] + } else{ + author <- NULL + } + } + + meta <- c(pkg$package, author) + } else { + meta <- NULL + } + ignore <- unique(c(meta, hunspell::en_stats, add_words)) + + # Create the hunspell dictionary object + dict <- hunspell::dictionary(lang, add_words = sort(ignore)) + + # Where to check for rmd/md files + bookdown_files <- list.files(file.path(path), pattern = "\\.rmd$", + ignore.case = TRUE, full.names = TRUE, recursive = TRUE) + root_files <- list.files(file.path(path), pattern = "(readme|news|changes).r?md", + ignore.case = TRUE, full.names = TRUE) + + # Markdown files + md_files <- sort(normalizePath(c(root_files, bookdown_files))) + md_lines <- lapply(md_files, spell_check_file_md, dict = dict) + + all_sources <- md_files + all_lines <- md_lines + + # Check 'DESCRIPTION' fields + if (file.exists(file.path(path, "DESCRIPTION"))){ + pkg_fields <- c("title", "description") + pkg <- as_package(path) + pkg_lines <- lapply(pkg_fields, function(x){ + if (x %in% names(pkg)) { + spell_check_file_text(textConnection(pkg[[x]]), dict = dict) + } else { + spell_check_file_text(textConnection(""), dict = dict) + } + }) + + all_sources <- c(all_sources, pkg_fields) + all_lines <- c(all_lines, pkg_lines) + } + + summarize_words(all_sources, all_lines) +} + diff --git a/R/spell-check.R b/R/spell-check.R index 38d9040..7812aec 100644 --- a/R/spell-check.R +++ b/R/spell-check.R @@ -95,6 +95,7 @@ as_package <- function(pkg){ } else { normalizePath(file.path(path, "DESCRIPTION"), mustWork = TRUE) } + pkg <- read.dcf(description)[1,] Encoding(pkg) = "UTF-8" pkg <- as.list(pkg) diff --git a/R/wordlist.R b/R/wordlist.R index 8288437..f854615 100644 --- a/R/wordlist.R +++ b/R/wordlist.R @@ -15,12 +15,12 @@ #' @family spelling #' @export #' @param confirm show changes and ask confirmation before adding new words to the list +#' @param pkg path to package (or bookdown folder) #' @inheritParams spell_check_package update_wordlist <- function(pkg = ".", vignettes = TRUE, confirm = TRUE){ - pkg <- as_package(pkg) - wordfile <- get_wordfile(pkg$path) - old_words <- sort(get_wordlist(pkg$path)) - new_words <- sort(spell_check_package(pkg$path, vignettes = vignettes, use_wordlist = FALSE)$word) + wordfile <- get_wordfile(pkg) + old_words <- sort(get_wordlist(pkg)) + new_words <- sort(spell_check_package(pkg, vignettes = vignettes, use_wordlist = FALSE)$word) if(isTRUE(all.equal(old_words, new_words))){ cat(sprintf("No changes required to %s\n", wordfile)) } else { @@ -51,8 +51,7 @@ update_wordlist <- function(pkg = ".", vignettes = TRUE, confirm = TRUE){ #' @rdname wordlist #' @export get_wordlist <- function(pkg = "."){ - pkg <- as_package(pkg) - wordfile <- get_wordfile(pkg$path) + wordfile <- get_wordfile(pkg) out <- if(file.exists(wordfile)) unlist(strsplit(readLines(wordfile, warn = FALSE, encoding = "UTF-8"), " ", fixed = TRUE)) as.character(out) diff --git a/_pkgdown.yml b/_pkgdown.yml new file mode 100644 index 0000000..1da054c --- /dev/null +++ b/_pkgdown.yml @@ -0,0 +1,11 @@ +reference: +- title: Utilities + desc: ~ + contents: + - '`spell_check_files`' + - '`wordlist`' +- title: Convenience functions + desc: ~ + contents: + - '`spell_check_package`' + - '`spell_check_bookdown`' diff --git a/man/spell_check_bookdown.Rd b/man/spell_check_bookdown.Rd new file mode 100644 index 0000000..b576f7f --- /dev/null +++ b/man/spell_check_bookdown.Rd @@ -0,0 +1,46 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/spell-check-bookdown.R +\name{spell_check_bookdown} +\alias{spell_check_bookdown} +\alias{spelling} +\title{Bookdown Spell Checking} +\usage{ +spell_check_bookdown(path = ".", lang = NULL, use_wordlist = TRUE) +} +\arguments{ +\item{path}{path to package root directory containing the \code{DESCRIPTION} file} + +\item{lang}{set \code{Language} field in \code{DESCRIPTION} e.g. \code{"en-US"} or \code{"en-GB"}. +For supporting other languages, see the \href{https://docs.ropensci.org/hunspell/articles/intro.html#hunspell-dictionaries}{hunspell vignette}.} + +\item{use_wordlist}{ignore words in the package \link[=get_wordlist]{WORDLIST} file} +} +\description{ +Automatically spell-check bookdown source files. +} +\details{ +Parses and checks bookdown source files, and if these files are present +\itemize{ +\item README.md (when there's no README.Rmd) +\item NEWS.md +\item text fields in the \code{DESCRIPTION} file if present. +} + +The preferred spelling language (typically \code{en-GB} or \code{en-US}) should be +\itemize{ +\item specified in the \code{Language} field from your package \code{DESCRIPTION}, +\item or entered as the \code{lang} argument. +To whitelist custom words +use the bookdown \link[=get_wordlist]{WORDLIST} file which will be added to the dictionary +when spell checking. See \link{update_wordlist} to automatically populate and update this +file. +} + +Hunspell includes dictionaries for \code{en_US} and \code{en_GB} by default. Other languages +require installation of a custom dictionary, see \link[hunspell:hunspell]{hunspell} for details. +} +\seealso{ +Other spelling: \code{\link{spell_check_files}}, + \code{\link{spell_check_package}}, \code{\link{wordlist}} +} +\concept{spelling} diff --git a/man/spell_check_files.Rd b/man/spell_check_files.Rd index 3437737..e464b45 100644 --- a/man/spell_check_files.Rd +++ b/man/spell_check_files.Rd @@ -38,7 +38,7 @@ files <- list.files(system.file("examples", package = "knitr"), spell_check_files(files) } \seealso{ -Other spelling: \code{\link{spell_check_package}}, - \code{\link{wordlist}} +Other spelling: \code{\link{spell_check_bookdown}}, + \code{\link{spell_check_package}}, \code{\link{wordlist}} } \concept{spelling} diff --git a/man/spell_check_package.Rd b/man/spell_check_package.Rd index 85d3869..d2a5f6b 100644 --- a/man/spell_check_package.Rd +++ b/man/spell_check_package.Rd @@ -48,7 +48,7 @@ Hunspell includes dictionaries for \code{en_US} and \code{en_GB} by default. Oth require installation of a custom dictionary, see \link[hunspell:hunspell]{hunspell} for details. } \seealso{ -Other spelling: \code{\link{spell_check_files}}, - \code{\link{wordlist}} +Other spelling: \code{\link{spell_check_bookdown}}, + \code{\link{spell_check_files}}, \code{\link{wordlist}} } \concept{spelling} diff --git a/man/wordlist.Rd b/man/wordlist.Rd index 8aa9670..4f9872a 100644 --- a/man/wordlist.Rd +++ b/man/wordlist.Rd @@ -11,7 +11,7 @@ update_wordlist(pkg = ".", vignettes = TRUE, confirm = TRUE) get_wordlist(pkg = ".") } \arguments{ -\item{pkg}{path to package root directory containing the \code{DESCRIPTION} file} +\item{pkg}{path to package (or bookdown folder)} \item{vignettes}{check all \code{rmd} and \code{rnw} files in the pkg root directory (e.g. \code{readme.md}) and package \code{vignettes} folder.} @@ -31,7 +31,8 @@ removes words from the wordlist that no longer appear as spelling errors, either they have been removed from the documentation or added to the \code{lang} dictionary. } \seealso{ -Other spelling: \code{\link{spell_check_files}}, +Other spelling: \code{\link{spell_check_bookdown}}, + \code{\link{spell_check_files}}, \code{\link{spell_check_package}} } \concept{spelling}