---
title: "Including Child Documents"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Including Child Documents}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
# comment to fix https://github.com/r-lib/pkgdown/issues/1843
knitr::opts_chunk$set(
    collapse = TRUE,
    compact = TRUE,
    comment = "#>"
)

# setup the lesson to include a child document
library("sandpaper")
library("pegboard")
snd <- asNamespace("sandpaper")
# do not use the package cache because it will slow us down
no_package_cache()
suppressMessages(lsn <- create_lesson(tempfile(), open = FALSE))

# Create the child file -----------------------------------------
txt <- c("## Session Information\n",
  "The following is the session information of this R session",
  "\n```{r sessioninfo}", "sessionInfo()", "```\n")
writeLines(txt, fs::path(lsn, "episodes", "files", "child.Rmd"))

# Append the episode --------------------------------------------
ep <- Episode$new(fs::path(lsn, "episodes", "introduction.Rmd"))
n <- length(xml2::xml_children(ep$body))
# we want to add the child just before the keypoints, which consist of the
# last three paragraphs
ep$add_md("```{r si, child = 'files/child.Rmd'}\n```\n", where = n - 3)
ep$write(fs::path(lsn, "episodes"), "Rmd")

# Load the lesson object ----------------------------------------
lsn_obj <- snd$this_lesson(lsn)
```


## Introduction

Writing a lesson using The Workbench consists of writing your lesson in markdown
or R Markdown files, placing them in the `episodes/` directory and defining the
order of those files in the `config.yaml` file. If you want to author a lesson
that contains content that is repeated across episodes, before {sandpaper}
version 0.14, you had to copy and paste the content across these documents,
which would be frustrating to update down the line. This is where [child
documents][child-doc] come in. You can save a repeated snippet of markdown
inside of a separate file and have R Markdown render the parent as if it had
that snippet embedded the whole time.

The only difference between child documents and the regular episodic content is
that these documents are not independent episodes in their own right. They do
not need to have a title, they do not need to be listed in the `config.yaml`,
they do not need any callout blocks. They only exist to be used by other
markdown documents. Because of this, it is NOT recommended to make these
documents overly complex.

[child-doc]: https://bookdown.org/yihui/rmarkdown-cookbook/child-document.html

## An Example: One Child Document

You can define these using the `child` attribute in code chunks in R Markdown
files (note: this requires R Markdown). You can use as many child documents
as you wish, but when you create child files, be sure the following two rules
apply:

1. The child documents live in the `files/` folder under their parent folders
2. The reference to child files is _relative_ to the parent file.

For example, you have a file structure like this:

```{r first-tree, echo = FALSE, comment = "#"}
withr::with_dir(lsn, fs::dir_tree("./episodes/"))
```

The last few lines of `episodes/introduction.Rmd` looks like this:

````markdown

```{r echo-parent, echo = FALSE, results = 'asis'}
lsn_obj$episodes[[1]]$tail(15)
```

````


You can see there the declaration of the child document is an empty code
chunk:

````{verbatim}

```{r si, child = "files/child.Rmd"}
```

````


This tells {knitr} to take a detour and render the contents of
`files/child.Rmd`(relative to the current working directory, which is the
episodes folder). The entire document of `episodes/files/child.Rmd` looks like
this:

````markdown

```{r echo-child, echo = FALSE, results = 'asis'}
lsn_obj$children[[1]]$show()
```

````

This means that when `episodes/introduction.Rmd` is built,
`episodes/files/child.Rmd` will also be built, evaulating the `sessionInfo()`.
So the resulting markdown document will look like this:

````markdown

```{r echo-built, echo = FALSE, results = 'asis'}
snd$build_markdown(lsn, rebuild = FALSE, quiet = TRUE, slug = "introduction")
lsn_obj$load_built()
lsn_obj$built[[1]]$tail(47)
```

````

## Workspace consideration

The {sandpaper} machinery aims to keep the working space as clean as possible
while still allowing authors to write in a way that feels natural. During
processing, assets (all `.R` files, everything in `files/`, `fig/`,
and `data/`) are copied over from their source folders to `site/built/`. This
allows us to build the R Markdown files using `site/built/` as the working
directory which accomplishes two things:

1. The default sandpaper folder structure is flattened for the website.
2. any files the R Markdown document creates or deletes will not affect the
   source files of the project

For example, let's say we added a chunk to our source R Markdown folder that
will create a file in the `data/` directory called "time.txt" and then reads in
the contents of that file.


````markdown

```{r add-writer, echo = FALSE, results = 'asis'}
ep <- lsn_obj$episodes[[1]]
txt <- c(
  "```{r setup, echo = FALSE}",
  "writeLines(format(Sys.time()), 'data/time.txt')",
  "cat(paste0('The time is: ', readLines('data/time.txt')))",
  "```"
)
ep$add_md(txt, 1)
ep$write(fs::path(lsn, "episodes"), format = "Rmd")
ep$head(10)
```

````

When the lesson gets built, the `episodes/` directory will continue to like
this:


```{r echo-episodes, echo = FALSE, comment = "#"}
snd$build_markdown(lsn, rebuild = FALSE, quiet = TRUE)
withr::with_dir(lsn, fs::dir_tree("./episodes/"))
```

Notice that there is no file called `data/time.txt`. This is because that file
was built inside the `site/built` directory. The contents of this directory
contains the output _along with all of the other files in the lesson_:

```{r echo-built-too, echo = FALSE, comment = "#"}
withr::with_dir(lsn, fs::dir_tree("./site/built/"))
```

You can see in `data/`, it now contains `time.txt`. The rendered Markdown file
`site/built/introduction.md` reflects the updates:

````markdown

```{r echo-time, echo = FALSE, results = 'asis'}
lsn_obj$load_built()
lsn_obj$built[["site/built/introduction.md"]]$head(10)
```

````

## Child document assets

When you use a child file, it is best to avoid relying on assets external to the
child document itself. If you do decide to reference other assets, **child
documents need to have assets written as if they were already embedded in the
_build parent_**. In this case, when I say _build parent_, I am refering to the
final parent of the child document where the output will eventually end up.

Let's say you wanted to refererence a figure `episodes/fig/one.png` and a link
to a document for learners `learners/info.md` from the child document, but
instead it's located at `episodes/files/children/child.Rmd`, which is built by
`episodes/introduction.Rmd`.

```{r setup-new-child, echo = FALSE, comment = "#"}
lsn_obj <- snd$this_lesson(lsn)
withr::with_dir(fs::path(lsn, "episodes", "files"), {
  fs::dir_create("children")
  fs::file_touch("../fig/one.png")
  writeLines(c(
      "---",
      "title: info",
      "---",
      "\nthis is a test file\n"
    ),
    con = "../../learners/info.md"
  )
  fs::file_move("child.Rmd", "children/child.Rmd")
})
# setup the child to use the correct code.
withr::with_dir(lsn, fs::dir_tree("./episodes/"))
ep <- lsn_obj$episodes[[1]]
code <- ep$code
xml2::xml_set_attr(code[[5]], "child",
  sQuote("files/children/child.Rmd", q = 2)
)
ep$write(fs::path(lsn, "episodes"), format = "Rmd")
lsn_obj <- snd$this_lesson(lsn)
lsn_obj$children[[1]]$add_md("
Here is a link to [the info document](../learners/info.md)

![example](fig/one.png){alt='example figure'}
")
lsn_obj$children[[1]]$write(fs::path(lsn, "episodes/files/children"),
  format = "Rmd"
)
```

The parent document (`episodes/introduction.Rmd`) referencing the child document
would look like this:

````markdown

```{r echo-parent-assets, echo = FALSE, results = 'asis'}
lsn_obj$episodes[[1]]$tail(15)
```

````

In `episodes/files/children/child.Rmd`, under [internal link
rules][internal-link-ref], you you should reference these
resources with `fig/one.png` and `../learners/info.md`, which are _in the
context of the build parent directory (in this case, `episodes/`)_, as
demonstrated here:

[internal-link-ref]: https://carpentries.github.io/sandpaper-docs/episodes.html#internal-links)


````markdown

```{r echo-child-assets, echo = FALSE, results = 'asis'}
lsn_obj$children[[1]]$show()
```

````

Again, the reason for this is because when the document gets built, the result
is all in the context of the parent document in the `site/built` directory[^1]\:

[^1]: there is a caveat to this paradigm, on translation to HTML, all links
  that begin with `../[folder]/` prefixes have those stripped in the final
  version of the site.


```{r show-built-assets, echo = FALSE, comment = "#"}
snd$build_markdown(lsn, rebuild = TRUE, quiet = TRUE)
withr::with_dir(lsn, fs::dir_tree("./site/built/"))
```

Which makes sense in `site/built/introduction.md`:

````markdown

```{r echo-built-assets, echo = FALSE, results = 'asis'}
lsn_obj$load_built()
lsn_obj$built[["site/built/introduction.md"]]$tail(50)
```

````

### What happens if we use relative links here?

If you use relative links in the child document, you will find warnings about
missing files during validation:

```{r, warn-missing, echo = FALSE}
child <- lsn_obj$children[[1]]
prepend_dest <- function(nodes, new = "../../") {
  dst <- xml2::xml_attr(nodes, "destination")
  xml2::xml_set_attr(nodes, "destination", paste0(new, dst))
}
prepend_dest(child$links)
prepend_dest(child$images)
child$write(fs::path(lsn, "episodes/files/children"), format = "Rmd")
snd$clear_this_lesson()
validate_lesson(lsn)
```



## Child documents of child documents

It is also possible to reference _other child documents_ within the child
document, but again, these **child documents paths must be relative to the
build parent path**


Thus, if you have a child document calling a second child document, you must
write the relative path from the build parent. This means that if you want to
reference `episodes/files/child-two.md` from `episodes/files/child-one.Rmd`,
you must write it this way: in `episodes/files/child-one.Rmd`:

````{verbatim}

```{r child='files/child-two.md'}
```

````


