diff --git a/news/changelog-1.6.md b/news/changelog-1.6.md index 441f0e42802..b6364334aa8 100644 --- a/news/changelog-1.6.md +++ b/news/changelog-1.6.md @@ -9,7 +9,7 @@ All changes included in 1.6: - ([#10039](https://github.com/quarto-dev/quarto-cli/issues/10039)): `quarto inspect` properly handles `!expr` tag in metadata. - ([#10188](https://github.com/quarto-dev/quarto-cli/issues/10188)): `quarto inspect` properly resolves includes across subdirectory boundaries. -## Lua Filters +## Lua Filters and extensions - ([#10004](https://github.com/quarto-dev/quarto-cli/issues/10004)): Resolve callout titles, theorem names, and `code-summary` content through `quarto_ast_pipeline()` and `process_shortcodes()`. - ([#10196](https://github.com/quarto-dev/quarto-cli/issues/10196)): Protect against nil values in `float.caption_long`. @@ -17,6 +17,7 @@ All changes included in 1.6: - ([#10624](https://github.com/quarto-dev/quarto-cli/issues/10624)): Don't crash when proof environments are empty in `pdf`. - ([#10858](https://github.com/quarto-dev/quarto-cli/issues/10858)): Don't crash in `gfm` when `content` of a `FloatRefTarget` is of type `Blocks`. - ([#10894](https://github.com/quarto-dev/quarto-cli/issues/10894)): Fix configuration of title and prefix in callouts for `html`, `revealjs`, `pdf`, and `typst`. +- ([#10999](https://github.com/quarto-dev/quarto-cli/issues/10999)): New API entry point: `quarto.paths.rscript()` to resolve `Rscript` path in Lua filters and extensions consistently with Quarto itself. ## `dashboard` Format diff --git a/src/command/render/filters.ts b/src/command/render/filters.ts index 4fca54a47fa..ec85b9dff01 100644 --- a/src/command/render/filters.ts +++ b/src/command/render/filters.ts @@ -66,7 +66,7 @@ import { QuartoFilterSpec } from "./types.ts"; import { Metadata } from "../../config/types.ts"; import { kProjectType } from "../../project/types.ts"; import { bibEngine } from "../../config/pdf.ts"; -import { resourcePath } from "../../core/resources.ts"; +import { rBinaryPath, resourcePath } from "../../core/resources.ts"; import { crossrefFilterActive, crossrefFilterParams } from "./crossref.ts"; import { layoutFilterParams } from "./layout.ts"; import { pandocMetadataPath } from "./render-paths.ts"; @@ -195,10 +195,19 @@ export async function filterParamsJson( [kShinyPythonExec]: isShinyPython ? await pythonExec() : undefined, [kExecutionEngine]: options.executionEngine, [kBrand]: options.format.render[kBrand], + "quarto-environment": await quartoEnvironmentParams(options), }; return JSON.stringify(params); } +async function quartoEnvironmentParams(_options: PandocOptions) { + return { + "paths": { + "Rscript": await rBinaryPath("Rscript"), + }, + }; +} + export function removeFilterParams(metadata: Metadata) { delete metadata[kQuartoParams]; } diff --git a/src/core/resources.ts b/src/core/resources.ts index 6ec52c1338f..f2522a89982 100644 --- a/src/core/resources.ts +++ b/src/core/resources.ts @@ -81,7 +81,23 @@ export function pandocBinaryPath(): string { : architectureToolsPath("pandoc"); } -export async function rBinaryPath(binary: string): Promise { +const _r_binary_path: Map = new Map(); +export async function rBinaryPath( + binary: string, + forceLookup = false, +): Promise { + if (forceLookup) { + _r_binary_path.delete(binary); + } + const v = _r_binary_path.get(binary); + if (v) { + return v; + } + const setPath = (path: string) => { + _r_binary_path.set(binary, path); + return path; + }; + debug(`-- Searching for R binary --`); // if there is a QUARTO_R environment variable then respect that const quartoR = Deno.env.get("QUARTO_R"); @@ -105,14 +121,14 @@ export async function rBinaryPath(binary: string): Promise { let rHomeBin = join(rHome, "bin", binary); if (safeExistsSync(rHomeBin)) { debug(`Found in ${rHomeBin}`); - return rHomeBin; + return setPath(rHomeBin); } if (Deno.build.os === "windows") { // Some installation have binaries in the sub folder only rHomeBin = join(rHome, "bin", "x64", binary); if (safeExistsSync(rHomeBin)) { debug(`Found in ${rHomeBin}`); - return rHomeBin; + return setPath(rHomeBin); } } } @@ -122,7 +138,7 @@ export async function rBinaryPath(binary: string): Promise { const path = await which(binary); if (path) { debug(`Found in PATH at ${path}`); - return path; + return setPath(path); } // on windows check the registry for a current version @@ -143,7 +159,7 @@ export async function rBinaryPath(binary: string): Promise { ); if (installPath) { debug(`Found in Windows Registry at ${join(installPath, "bin")}`); - return join(installPath, "bin", binary); + return setPath(join(installPath, "bin", binary)); } } // last ditch, try to find R in program files @@ -157,7 +173,7 @@ export async function rBinaryPath(binary: string): Promise { for (const walk of walkSync(join(progFiles, "R"))) { if (walk.isDirectory && walk.name === "bin") { debug(`Found ${walk.path}`); - return join(walk.path, binary); + return setPath(join(walk.path, binary)); } } } @@ -167,7 +183,7 @@ export async function rBinaryPath(binary: string): Promise { // We couldn't find R, just pass the binary itself and hope it works out! debug(`Quarto did no found ${binary} and will try to use it directly.`); - return binary; + return setPath(binary); } export function projectTypeResourcePath(projectType: string) { diff --git a/src/resources/lua-types/quarto/paths.lua b/src/resources/lua-types/quarto/paths.lua new file mode 100644 index 00000000000..c5446997559 --- /dev/null +++ b/src/resources/lua-types/quarto/paths.lua @@ -0,0 +1,9 @@ +---@meta + +quarto.paths = {} + +--[[ +Returns the path to the `Rscript` file that Quarto itself would use in its knitr engine. +]] +---@return string # Path to `Rscript` file +function quarto.paths.rscript() end diff --git a/src/resources/pandoc/datadir/init.lua b/src/resources/pandoc/datadir/init.lua index f6f0c472531..0cde1cccedd 100644 --- a/src/resources/pandoc/datadir/init.lua +++ b/src/resources/pandoc/datadir/init.lua @@ -2072,24 +2072,30 @@ quarto = { crossref = {} }, project = { - directory = projectDirectory(), - offset = projectOffset(), - profile = pandoc.List(projectProfiles()), - output_directory = projectOutputDirectory() + directory = projectDirectory(), + offset = projectOffset(), + profile = pandoc.List(projectProfiles()), + output_directory = projectOutputDirectory() }, utils = { - dump = utils.dump, - table = utils.table, - type = utils.type, - resolve_path = resolvePathExt, - resolve_path_relative_to_document = resolvePath, - as_inlines = utils.as_inlines, - as_blocks = utils.as_blocks, - string_to_blocks = utils.string_to_blocks, - string_to_inlines = utils.string_to_inlines, - render = utils.render, - match = utils.match, - add_to_blocks = utils.add_to_blocks + dump = utils.dump, + table = utils.table, + type = utils.type, + resolve_path = resolvePathExt, + resolve_path_relative_to_document = resolvePath, + as_inlines = utils.as_inlines, + as_blocks = utils.as_blocks, + string_to_blocks = utils.string_to_blocks, + string_to_inlines = utils.string_to_inlines, + render = utils.render, + match = utils.match, + add_to_blocks = utils.add_to_blocks, + }, + paths = { + rscript = function() + -- matches the path from `quartoEnvironmentParams` from src/command/render/filters.ts + return param('quarto-environment', nil).paths.Rscript + end, }, json = json, base64 = base64, diff --git a/tests/docs/smoke-all/2024/10/07/issue-10999.lua b/tests/docs/smoke-all/2024/10/07/issue-10999.lua new file mode 100644 index 00000000000..1089f03d088 --- /dev/null +++ b/tests/docs/smoke-all/2024/10/07/issue-10999.lua @@ -0,0 +1,5 @@ +function Pandoc(doc) + if quarto.paths.rscript() == nil then + crash() + end +end \ No newline at end of file diff --git a/tests/docs/smoke-all/2024/10/07/issue-10999.qmd b/tests/docs/smoke-all/2024/10/07/issue-10999.qmd new file mode 100644 index 00000000000..2d15fd066ad --- /dev/null +++ b/tests/docs/smoke-all/2024/10/07/issue-10999.qmd @@ -0,0 +1,7 @@ +--- +title: issue-10999 +filters: + - issue-10999.lua +--- + +## hello. \ No newline at end of file