Skip to content

Commit 316167c

Browse files
authored
Merge branch 'main' into preview-url-verbose
2 parents 2d488a5 + d06e627 commit 316167c

25 files changed

+598
-19
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: quarto
22
Title: R Interface to 'Quarto' Markdown Publishing System
3-
Version: 1.4.4.9019
3+
Version: 1.4.4.9022
44
Authors@R: c(
55
person("JJ", "Allaire", , "[email protected]", role = "aut",
66
comment = c(ORCID = "0000-0003-0174-9868")),

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Generated by roxygen2: do not edit by hand
22

3+
export(add_spin_preamble)
34
export(check_newer_version)
45
export(is_using_quarto)
56
export(new_blog_post)

NEWS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
- `quarto_preview()` now explicitly returns the preview server URL (invisibly) and documents this behavior. This enables programmatic workflows such as taking screenshots with **webshot2** or passing the URL to other automation tools (thanks, @cwickham, #233).
44

5+
- Added `add_spin_preamble()` function to add YAML preambles to R scripts for use with Quarto Script rendering support. The function automatically detects existing preambles and provides flexible customization options through `title` and `preamble` parameters (#164).
6+
7+
- `quarto_create_project()` gains a `title` argument to set the project title independently from the directory name. This allows creating projects with custom titles, including when using `name = "."` to create a project in the current directory (thanks, @davidkane9, #148). This matches with `--title` addition for `quarto create project` in Quarto CLI v1.5.15.
8+
9+
- `quarto_use_template()` now supports using templates in another empty directory via the `dir` argument. However, the function will fail with a clear error message when used in non-empty directories, as interactive prompting is required and handled by Quarto CLI directly (requires Quarto 1.5.15+). Follow for [quarto-dev/quarto-cli#11127](https://github.com/quarto-dev/quarto-cli/issues/11127) for change with `--no-prompt` behavior in future Quarto versions.
10+
511
- `quarto_render(output_file = )` now sets the `output-file` Quarto metadata instead of the `--output` CLI flag. This allows the output file information to correctly be processed by Quarto, as if passed in a YAML header. e.g. it allows to support multiple output formats in the same render call. `quarto_render(quarto_args = c('--output', 'dummy.html'))` can still be used to set the `--output` CLI flag to enforce using the CLI flag and not the metadata processed by Quarto (#251, #43).
612

713
- Added `check_newer_version()` function to check if a newer version of Quarto is available. The function compares the current Quarto version against the latest stable and prerelease versions. It is aimed for verbosity by default (`verbose = TRUE`), but `verbose = FALSE` can also be set for just checking update availability with TRUE or FALSE return values. Version information is cached per session for up to 24 hours to minimize network requests.

R/blog.R

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ make_post_yaml <- function(title, ...) {
8989
if (length(yml_values$categories) == 0) {
9090
yml_values <- yml_values[names(yml_values) != "categories"]
9191
}
92-
yml_values <- as_yaml(yml_values)
93-
yml_values <- paste0("---\n", yml_values, "---\n")
92+
yml_values <- as_yaml_block(yml_values)
9493
yml_values
9594
}
9695

R/create.R

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
#'
1212
#' @param type The type of project to create. As of Quarto 1.4, it can be one of
1313
#' `r paste0("\\code{", paste(quarto_project_type, collapse = "}, \\code{"),"}")`.
14-
#' @param name The name of the project and the directory that will be created.
14+
#' @param name The name of the project and the directory that will be created. Special case
15+
#' is to use `name = "."` to create the project in the current directory. In that case provide `title`
16+
#' to set the project title.
17+
#' @param title The title of the project. By default, it will be the name of the project, same as directory name created.
18+
#' or "My project" if `name = "."`. If you want to set a different title, provide it here.
1519
#' @param dir The directory in which to create the new Quarto project, i.e. the
1620
#' parent directory.
1721
#'
@@ -23,7 +27,17 @@
2327
#'
2428
#' @examples
2529
#' \dontrun{
30+
#' # Create a new project directory in another directory
2631
#' quarto_create_project("my-first-quarto-project", dir = "~/tmp")
32+
#'
33+
#' # Create a new project directory in the current directory
34+
#' quarto_create_project("my-first-quarto-project")
35+
#'
36+
#' # Create a new project with a different title
37+
#' quarto_create_project("my-first-quarto-project", title = "My Quarto Project")
38+
#'
39+
#' # Create a new project inside the current directory directly
40+
#' quarto_create_project(".", title = "My Quarto Project")
2741
#' }
2842
#'
2943
#'
@@ -32,23 +46,41 @@ quarto_create_project <- function(
3246
name,
3347
type = "default",
3448
dir = ".",
49+
title = name,
3550
no_prompt = FALSE,
3651
quiet = FALSE,
3752
quarto_args = NULL
3853
) {
3954
check_quarto_version(
4055
"1.4",
41-
"quarto create project",
56+
"quarto create project <type> <name>",
4257
"https://quarto.org/docs/projects/quarto-projects.html"
4358
)
4459

4560
if (rlang::is_missing(name)) {
4661
cli::cli_abort("You need to provide {.arg name} for the new project.")
4762
}
4863

64+
# If title is provided, check for Quarto version 1.5.15 or higher
65+
if (title != name) {
66+
check_quarto_version(
67+
"1.5.15",
68+
"quarto create project <type> <name> <title>",
69+
"https://quarto.org/docs/projects/quarto-projects.html"
70+
)
71+
}
72+
4973
if (rlang::is_interactive() && !no_prompt) {
74+
folder_msg <- if (name != ".") {
75+
"as a folder named {.strong {name}}"
76+
}
5077
cli::cli_inform(c(
51-
"This will create a new Quarto {.emph {type}} project as a folder named {.strong {name}} in {.path {xfun::normalize_path(dir)}}."
78+
paste(
79+
"This will create a new Quarto {.emph {type}} project",
80+
folder_msg,
81+
"in {.path {xfun::normalize_path(dir)}}."
82+
),
83+
"Project title will be set to {.strong {title}}."
5284
))
5385
prompt_value <- tolower(readline(sprintf("Do you want to proceed (Y/n)? ")))
5486
if (!prompt_value %in% c("", "y")) {
@@ -62,7 +94,7 @@ quarto_create_project <- function(
6294
"project",
6395
type,
6496
name,
65-
name,
97+
title,
6698
"--no-prompt",
6799
"--no-open",
68100
if (is_quiet(quiet)) cli_arg_quiet(),

R/metadata.R

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ write_yaml_metadata_block <- function(..., .list = NULL) {
8989
if (length(meta) == 0) {
9090
return()
9191
}
92-
res <- as_yaml(meta)
93-
yaml_block <- paste0("---\n", res, "---\n")
92+
yaml_block <- as_yaml_block(meta)
9493
knitr::asis_output(yaml_block)
9594
}

R/spin.R

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#' Add spin preamble to R script
2+
#'
3+
#' Adds a minimal spin preamble to an R script file if one doesn't already exist.
4+
#' The preamble includes a title derived from the filename and is formatted as
5+
#' a YAML block suitable preprended with `#'` for [knitr::spin()].
6+
#'
7+
#' This is useful to prepare R scripts for use with
8+
#' Quarto Script rendering support.
9+
#' See <https://quarto.org/docs/computations/render-scripts.html#knitr>
10+
#'
11+
#' @section Preamble format:
12+
#' For a script named `analysis.R`, the function adds this preamble by default:
13+
#' ```
14+
#' #' ---
15+
#' #' title: analysis
16+
#' #' ---
17+
#' #'
18+
#'
19+
#' # Original script content starts here
20+
#' ```
21+
#'
22+
#' This is the minimal preamble required for Quarto Script rendering, so that
23+
#' [Engine Bindings](https://quarto.org/docs/computations/execution-options.html#engine-binding) works.
24+
#'
25+
#' @param script Path to the R script file
26+
#' @param title Custom title for the preamble. If provided, overrides any title
27+
#' in the `preamble` list. If NULL, uses `preamble$title` or filename as fallback.
28+
#' @param preamble Named list of YAML metadata to include in preamble.
29+
#' The `title` parameter takes precedence over `preamble$title` if both are provided.
30+
#' @return Invisibly returns the script path if modified, otherwise invisible NULL
31+
#'
32+
#' @examples
33+
#' \dontrun{
34+
#' # Basic usage with default title
35+
#' add_spin_preamble("analysis.R")
36+
#'
37+
#' # Custom title
38+
#' add_spin_preamble("analysis.R", title = "My Analysis")
39+
#'
40+
#' # Custom preamble with multiple fields
41+
#' add_spin_preamble("analysis.R", preamble = list(
42+
#' title = "Advanced Analysis",
43+
#' author = "John Doe",
44+
#' date = Sys.Date(),
45+
#' format = "html"
46+
#' ))
47+
#'
48+
#' # Title parameter overrides preamble title
49+
#' add_spin_preamble("analysis.R",
50+
#' title = "Final Title", # This takes precedence
51+
#' preamble = list(
52+
#' title = "Ignored Title",
53+
#' author = "John Doe"
54+
#' )
55+
#' )
56+
#' }
57+
#' @export
58+
add_spin_preamble <- function(script, title = NULL, preamble = NULL) {
59+
if (!fs::file_exists(script)) {
60+
cli::cli_abort(c(
61+
"File {.file {script}} does not exist.",
62+
"Please provide a valid file path."
63+
))
64+
}
65+
66+
content <- xfun::read_utf8(script)
67+
68+
# if files starts with a spin preamble, do nothing
69+
if (grepl("^\\s*#'", content[1])) {
70+
cli::cli_inform(c(
71+
"File {.file {script}} already has a spin preamble.",
72+
"No changes made. Edit manually if needed."
73+
))
74+
return(invisible())
75+
}
76+
77+
# Build preamble metadata
78+
metadata <- list()
79+
80+
# Start with preamble list if provided
81+
if (!is.null(preamble)) {
82+
if (!is.list(preamble)) {
83+
cli::cli_abort("`preamble` must be a named list.")
84+
}
85+
metadata <- preamble
86+
}
87+
88+
# Add or override title
89+
if (!is.null(title)) {
90+
metadata$title <- title
91+
} else if (is.null(metadata$title)) {
92+
# Use filename as default title if none provided
93+
metadata$title <- fs::path_file(fs::path_ext_remove(script))
94+
}
95+
96+
preamble_text <- paste(
97+
"#'",
98+
xfun::split_lines(as_yaml_block(metadata))
99+
)
100+
101+
new_content <- c(preamble_text, "", content)
102+
xfun::write_utf8(new_content, con = script)
103+
104+
cli::cli_inform("Added spin preamble to {.file {script}}")
105+
return(invisible(script))
106+
}

R/use.R

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,42 @@
88
#' @param template The template to install, either an archive or a GitHub
99
#' repository as described in the documentation
1010
#' <https://quarto.org/docs/extensions/formats.html>.
11-
#'
11+
#' @param dir The directory in which to install the template. This must be an empty directory.
12+
#' To use directly in a non-empty directory, use `quarto use template` interactively in the terminal for safe installation
13+
#' without overwrite.
1214
#' @param quiet Suppress warnings and messages.
1315
#'
1416
#'
1517
#' @examples
1618
#' \dontrun{
17-
#' # Install a template and set up a draft document from a GitHub repository
19+
#' # Use a template and set up a draft document from a GitHub repository
1820
#' quarto_use_template("quarto-journals/jss")
1921
#'
20-
#' # Install a template and set up a draft document from a ZIP archive
22+
#' # Use a template in current directory by installing it in an empty directory
23+
#' quarto_use_template("quarto-journals/jss", dir = "new-empty-dir")
24+
#'
25+
#' # Use a template and set up a draft document from a ZIP archive
2126
#' quarto_use_template("https://github.com/quarto-journals/jss/archive/refs/heads/main.zip")
2227
#' }
2328
#'
2429
#' @export
2530
quarto_use_template <- function(
2631
template,
32+
dir = ".",
2733
no_prompt = FALSE,
2834
quiet = FALSE,
2935
quarto_args = NULL
3036
) {
3137
rlang::check_required(template)
3238

39+
if (!is_empty_dir(dir) && quarto_available("1.5.15")) {
40+
cli::cli_abort(c(
41+
"{.arg dir} must be an empty directory.",
42+
"x" = "The directory {.path {dir}} is not empty.",
43+
" " = "Please provide an empty directory or use {.code quarto use template {template} } interactively in terminal."
44+
))
45+
}
46+
3347
quarto_bin <- find_quarto()
3448

3549
# This will ask for approval or stop installation
@@ -47,8 +61,10 @@ quarto_use_template <- function(
4761
if (quarto_version() > "1.5.4" && is_quiet(quiet)) {
4862
args <- cli_arg_quiet(args)
4963
}
50-
51-
quarto_use(args, quarto_bin = quarto_bin, echo = !quiet)
64+
xfun::in_dir(
65+
dir,
66+
quarto_use(args, quarto_bin = quarto_bin, echo = !quiet)
67+
)
5268
}
5369

5470
invisible()

R/utils.R

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ write_yaml <- function(x, file) {
2424
yaml::write_yaml(x, file, handlers = yaml_handlers)
2525
}
2626

27+
as_yaml_block <- function(x) {
28+
# Convert to YAML and wrap in a block
29+
yaml_content <- as_yaml(x)
30+
paste0("---\n", yaml_content, "---\n")
31+
}
32+
2733

2834
# inline knitr:::merge_list()
2935
merge_list <- function(x, y) {
@@ -65,3 +71,11 @@ has_internet <- function(host = "https://www.google.com") {
6571
}
6672
)
6773
}
74+
75+
is_empty_dir <- function(dir) {
76+
if (!dir.exists(dir)) {
77+
return(FALSE)
78+
}
79+
files <- list.files(dir, all.files = TRUE, no.. = TRUE)
80+
length(files) == 0
81+
}

_pkgdown.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,4 @@ reference:
6868
These functions are used to help with Quarto documents and projects:
6969
contents:
7070
- write_yaml_metadata_block
71+
- add_spin_preamble

0 commit comments

Comments
 (0)