[med-svn] [r-cran-rprojroot] 01/02: New upstream version 1.2

Andreas Tille tille at debian.org
Sun Oct 1 22:11:02 UTC 2017


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository r-cran-rprojroot.

commit f0ff916eba451d3fde04d83c58c89e26be7c696d
Author: Andreas Tille <tille at debian.org>
Date:   Mon Oct 2 00:10:34 2017 +0200

    New upstream version 1.2
---
 DESCRIPTION                                        |  28 ++
 MD5                                                |  38 +++
 NAMESPACE                                          |  35 +++
 NEWS.md                                            |  71 +++++
 R/criterion.R                                      | 151 ++++++++++
 R/file.R                                           |  32 ++
 R/has-file.R                                       | 249 ++++++++++++++++
 R/root.R                                           |  85 ++++++
 R/rprojroot-package.R                              |  15 +
 R/rrmake.R                                         |  13 +
 R/shortcut.R                                       |  15 +
 build/vignette.rds                                 | Bin 0 -> 225 bytes
 inst/doc/rprojroot.R                               |  94 ++++++
 inst/doc/rprojroot.Rmd                             | 301 +++++++++++++++++++
 inst/doc/rprojroot.html                            | 331 +++++++++++++++++++++
 man/criteria.Rd                                    |  62 ++++
 man/find_root.Rd                                   |  47 +++
 man/find_root_file.Rd                              |  54 ++++
 man/root_criterion.Rd                              | 120 ++++++++
 man/rprojroot-package.Rd                           |  39 +++
 tests/testthat.R                                   |   4 +
 tests/testthat/hierarchy/DESCRIPTION               |   9 +
 tests/testthat/hierarchy/a/b/a                     |   0
 tests/testthat/hierarchy/a/b/b                     |   1 +
 tests/testthat/hierarchy/a/b/c/d                   |   0
 tests/testthat/hierarchy/a/remake.yml              |   0
 tests/testthat/hierarchy/b                         |   1 +
 tests/testthat/hierarchy/c                         |   0
 tests/testthat/hierarchy/hierarchy.Rproj           |  21 ++
 tests/testthat/package/DESCRIPTION                 |   0
 tests/testthat/package/tests/testthat.R            |   0
 .../package/tests/testthat/test-something.R        |   0
 tests/testthat/test-criterion.R                    |  49 +++
 tests/testthat/test-make.R                         |  13 +
 tests/testthat/test-root.R                         | 197 ++++++++++++
 tests/testthat/test-testthat.R                     |  17 ++
 tests/testthat/vcs/git.zip                         | Bin 0 -> 22155 bytes
 tests/testthat/vcs/svn.zip                         | Bin 0 -> 10116 bytes
 vignettes/rprojroot.Rmd                            | 301 +++++++++++++++++++
 39 files changed, 2393 insertions(+)

diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100644
index 0000000..52b5a95
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,28 @@
+Package: rprojroot
+Title: Finding Files in Project Subdirectories
+Version: 1.2
+Authors at R: person(given = "Kirill", family = "Müller", role = c("aut",
+        "cre"), email = "krlmlr+r at mailbox.org")
+Description: Robust, reliable and flexible paths to files below a
+    project root. The 'root' of a project is defined as a directory
+    that matches a certain criterion, e.g., it contains a certain
+    regular file.
+Depends: R (>= 3.0.0)
+Imports: backports
+Suggests: testthat, knitr, withr, rmarkdown
+VignetteBuilder: knitr
+License: GPL-3
+LazyData: true
+Encoding: UTF-8
+URL: https://github.com/krlmlr/rprojroot,
+        https://krlmlr.github.io/rprojroot
+BugReports: https://github.com/krlmlr/rprojroot/issues
+RoxygenNote: 5.0.1.9000
+Collate: 'rrmake.R' 'criterion.R' 'file.R' 'has-file.R' 'root.R'
+        'rprojroot-package.R' 'shortcut.R'
+NeedsCompilation: no
+Packaged: 2017-01-16 11:50:23 UTC; muelleki
+Author: Kirill Müller [aut, cre]
+Maintainer: Kirill Müller <krlmlr+r at mailbox.org>
+Repository: CRAN
+Date/Publication: 2017-01-16 14:16:04
diff --git a/MD5 b/MD5
new file mode 100644
index 0000000..c64e4b3
--- /dev/null
+++ b/MD5
@@ -0,0 +1,38 @@
+09fc8e111b014442f6569ac39cfff43f *DESCRIPTION
+04765b617541cee07297676a210d25d3 *NAMESPACE
+2d0469a219c83ed66c4e2695d4ff2d1c *NEWS.md
+da5b8d6d5d1ea8a0aa1c4a1cfed314ff *R/criterion.R
+3283d4a6f17606ad0193bace804ed012 *R/file.R
+1050ecdd048dce13bbf81615de85489f *R/has-file.R
+d87a8501549770bd80068475e78671fe *R/root.R
+0688a81a507c7639e2b2cec0edad2dbf *R/rprojroot-package.R
+dd227290335dea57e2a1031a5b5beb35 *R/rrmake.R
+4de9a9bbf48372e1505dd1550bee0418 *R/shortcut.R
+0415d9530ea3a983814164d735e5db9e *build/vignette.rds
+0634afa63b0e9c96760bcdce54655942 *inst/doc/rprojroot.R
+7855fbb459b9e137bc458294b20db80c *inst/doc/rprojroot.Rmd
+9e32800aeeef65fcddc218dcaa367682 *inst/doc/rprojroot.html
+6a14df38876e8de0b3a24e689aa98cf7 *man/criteria.Rd
+df64533edc92ee73830a13070b418d54 *man/find_root.Rd
+5accd629c3b4aa22c6a484b2a52ed0a2 *man/find_root_file.Rd
+4a89be5e4f191328f2a7e8b6f31a8ba6 *man/root_criterion.Rd
+a7d033b76a25c7abfb137de00b3da294 *man/rprojroot-package.Rd
+1d802d92f687ffbb11ee26ae68396d94 *tests/testthat.R
+3e6320af0b5c1a4ab2bc220759c31553 *tests/testthat/hierarchy/DESCRIPTION
+d41d8cd98f00b204e9800998ecf8427e *tests/testthat/hierarchy/a/b/a
+b16de7308a1f8d6f4f98b1a558ea957d *tests/testthat/hierarchy/a/b/b
+d41d8cd98f00b204e9800998ecf8427e *tests/testthat/hierarchy/a/b/c/d
+d41d8cd98f00b204e9800998ecf8427e *tests/testthat/hierarchy/a/remake.yml
+55e116ea754236d78e4e6342adbe3661 *tests/testthat/hierarchy/b
+d41d8cd98f00b204e9800998ecf8427e *tests/testthat/hierarchy/c
+3936ed002c1dd1fa5dc7a13d99d5df86 *tests/testthat/hierarchy/hierarchy.Rproj
+d41d8cd98f00b204e9800998ecf8427e *tests/testthat/package/DESCRIPTION
+d41d8cd98f00b204e9800998ecf8427e *tests/testthat/package/tests/testthat.R
+d41d8cd98f00b204e9800998ecf8427e *tests/testthat/package/tests/testthat/test-something.R
+06e74a052aa3a2ab9a51a5135802a55b *tests/testthat/test-criterion.R
+ce3ca96d5d38e1c0972d639a81f659c1 *tests/testthat/test-make.R
+006eb32f50098d057c0a9cfb6ac4d0ba *tests/testthat/test-root.R
+ec33c4973b9fac9b680322d60d6807ac *tests/testthat/test-testthat.R
+d8879d25ee21b0f84992020d95500aaf *tests/testthat/vcs/git.zip
+28e9555632b66c264d026cd065dda0db *tests/testthat/vcs/svn.zip
+7855fbb459b9e137bc458294b20db80c *vignettes/rprojroot.Rmd
diff --git a/NAMESPACE b/NAMESPACE
new file mode 100644
index 0000000..9dafdaf
--- /dev/null
+++ b/NAMESPACE
@@ -0,0 +1,35 @@
+# Generated by roxygen2: do not edit by hand
+
+S3method("|",root_criterion)
+S3method(as.root_criterion,character)
+S3method(as.root_criterion,default)
+S3method(as.root_criterion,root_criterion)
+S3method(format,root_criterion)
+S3method(print,root_criterion)
+S3method(str,root_criteria)
+export(as.root_criterion)
+export(criteria)
+export(find_package_root_file)
+export(find_remake_root_file)
+export(find_root)
+export(find_root_file)
+export(find_rstudio_root_file)
+export(find_testthat_root_file)
+export(from_wd)
+export(get_root_desc)
+export(has_dir)
+export(has_dirname)
+export(has_file)
+export(has_file_pattern)
+export(is.root_criterion)
+export(is_git_root)
+export(is_projectile_project)
+export(is_r_package)
+export(is_remake_project)
+export(is_rstudio_project)
+export(is_svn_root)
+export(is_testthat)
+export(is_vcs_root)
+export(root_criterion)
+import(backports)
+importFrom(utils,str)
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
index 0000000..18da147
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,71 @@
+# rprojroot 1.2 (2017-01-15)
+
+- New root criteria
+    - `is_projectile_project` recognize projectile projects (#21).
+    - `has_dir()` constructs root criteria that check for existence of a directory.
+    - `is_git_root`, `is_svn_root` and `is_vcs_root` look for a version control system root (#19).
+
+- New function
+    - `get_root_desc()` returns the description of the criterion that applies to a given root, useful for composite criteria created with `|`.
+
+- Minor enhancements
+    - Improve formatting of alternative criteria (#18).
+    - If root cannot be found, the start path is shown in the error message.
+
+- Internal
+    - The `$testfun` member of the `rprojroot` S3 class is now a list of functions instead of a function.
+
+
+# rprojroot 1.1 (2016-10-29)
+
+- Compatibility
+    - Compatible with R >= 3.0.0 with the help of the `backports` package.
+
+- New root criteria
+    - `is_remake_project` and `find_remake_root_file()` look for [remake](https://github.com/richfitz/remake) project (#17).
+    - `is_testthat` and `find_testthat_root_file()` that looks for `tests/testthat` root (#14).
+    - `from_wd`, useful for creating accessors to a known path (#11).
+
+- Minor enhancement
+    - Criteria can be combined with the `|` operator (#15).
+
+- Documentation
+    - Add package documentation with a few examples (#13).
+    - Clarify difference between `find_file()` and `make_fix_file()` in vignette (#9).
+    - Remove unexported functions from documentation and examples (#10).
+    - Use `pkgdown` to create website.
+
+- Testing
+    - Use Travis instead of wercker. Travis tests three R versions, and OS X.
+    - Improve AppVeyor testing.
+
+
+# rprojroot 1.0-2 (2016-03-28)
+
+- Fix test that fails on Windows only on CRAN.
+
+
+# rprojroot 1.0 (2016-03-26)
+
+Initial CRAN release.
+
+- S3 class `root_criterion`:
+    - Member functions: `find_file()` and `make_fix_file()`
+    - `root_criterion()`
+    - `as.root_criterion()`
+    - `is.root_criterion()`
+    - `has_file()`
+    - `has_file_pattern()`
+    - Built-in criteria:
+        - `is_r_package`
+        - `is_rstudio_project`
+
+- Getting started:
+    - `find_package_root_file()`
+    - `find_rstudio_root_file()`
+
+- Use a custom notion of a project root:
+    - `find_root()`
+    - `find_root_file()`
+
+- Vignette
diff --git a/R/criterion.R b/R/criterion.R
new file mode 100644
index 0000000..35dd60c
--- /dev/null
+++ b/R/criterion.R
@@ -0,0 +1,151 @@
+#' Is a directory the project root?
+#'
+#' Objects of the `root_criterion` class decide if a
+#' given directory is a project root.
+#'
+#' Construct criteria using `root_criterion` in a very general fashion
+#' by specifying a function with a `path` argument, and a description.
+#'
+#' @param testfun A function with one parameter that returns `TRUE`
+#'   if the directory specified by this parameter is the project root,
+#'   and `FALSE` otherwise. Can also be a list of such functions.
+#' @param desc A textual description of the test criterion, of the same length
+#'   as `testfun`
+#' @param subdir Subdirectories to start the search in, if found
+#'
+#' @return
+#' An S3 object of class `root_criterion` wit the following members:
+#'
+#' @include rrmake.R
+#' @export
+#'
+#' @examples
+#' root_criterion(function(path) file.exists(file.path(path, "somefile")), "has somefile")
+#' has_file("DESCRIPTION")
+#' is_r_package
+#' is_r_package$find_file
+#' \dontrun{
+#' is_r_package$make_fix_file(".")
+#' }
+root_criterion <- function(testfun, desc, subdir = NULL) {
+  testfun <- check_testfun(testfun)
+
+  stopifnot(length(desc) == length(testfun))
+
+  full_desc <- paste0(
+    desc,
+    if (!is.null(subdir)) paste0(
+      " (also look in subdirectories: ",
+      paste0("`", subdir, "`", collapse = ", "),
+      ")"
+    )
+  )
+
+  criterion <- structure(
+    list(
+      #' @return
+      #' \describe{
+      #'   \item{`testfun`}{The `testfun` argument}
+      testfun = testfun,
+      #'   \item{`desc`}{The `desc` argument}
+      desc = full_desc,
+      #'   \item{`subdir`}{The `subdir` argument}
+      subdir = subdir
+    ),
+    class = "root_criterion"
+  )
+
+  #'   \item{`find_file`}{A function with `...` argument that returns
+  #'     for a path relative to the root specified by this criterion.
+  #'     The optional `path` argument specifies the starting directory,
+  #'     which defaults to `"."`.
+  #'   }
+  criterion$find_file <- make_find_root_file(criterion)
+  #'   \item{`make_fix_file`}{A function with a `path` argument that
+  #'      returns a function that finds paths relative to the root.  For a
+  #'      criterion `cr`, the result of `cr$make_fix_file(".")(...)`
+  #'      is identical to `cr$find_file(...)`. The function created by
+  #'      `make_fix_file` can be saved to a variable to be more independent
+  #'      of the current working directory.
+  #'   }
+  #' }
+  criterion$make_fix_file <-
+    function(path = getwd()) make_fix_root_file(criterion, path)
+
+  criterion
+}
+
+check_testfun <- function(testfun) {
+  if (is.function(testfun)) {
+    testfun <- list(testfun)
+  }
+
+  for (f in testfun) {
+    if (!isTRUE(all.equal(names(formals(f)), "path"))) {
+      stop("All functions in testfun must have exactly one argument 'path'")
+    }
+  }
+
+  testfun
+}
+
+#' @rdname root_criterion
+#' @param x An object
+#' @export
+is.root_criterion <- function(x) {
+  inherits(x, "root_criterion")
+}
+
+#' @rdname root_criterion
+#' @export
+as.root_criterion <- function(x) UseMethod("as.root_criterion", x)
+
+#' @details
+#' The `as.root_criterion` function accepts objects of class
+#' `root_criterion`, and character values; the latter will be
+#' converted to criteria using `has_file`.
+#'
+#' @rdname root_criterion
+#' @export
+as.root_criterion.character <- function(x) {
+  has_file(x)
+}
+
+#' @rdname root_criterion
+#' @export
+as.root_criterion.root_criterion <- identity
+
+#' @export
+as.root_criterion.default <- function(x) {
+  stop("Cannot coerce ", x, " to type root_criterion.")
+}
+
+#' @export
+format.root_criterion <- function(x, ...) {
+  if (length(x$desc) > 1) {
+    c("Root criterion: one of", paste0("- ", x$desc))
+  } else {
+    paste0("Root criterion: ", x$desc)
+  }
+}
+
+#' @export
+print.root_criterion <- function(x, ...) {
+  cat(format(x), sep = "\n")
+  invisible(x)
+}
+
+#' @export
+#' @rdname root_criterion
+#' @details Root criteria can be combined with the `|` operator. The result is a
+#'   composite root criterion that requires either of the original criteria to
+#'   match.
+#' @param y An object
+`|.root_criterion` <- function(x, y) {
+  stopifnot(is.root_criterion(y))
+
+  root_criterion(
+    c(x$testfun, y$testfun),
+    c(x$desc, y$desc)
+  )
+}
diff --git a/R/file.R b/R/file.R
new file mode 100644
index 0000000..45ad46f
--- /dev/null
+++ b/R/file.R
@@ -0,0 +1,32 @@
+#' File paths relative to the root of a directory hierarchy
+#'
+#' Append an arbitrary number of path components to the root using
+#' [base::file.path()].
+#'
+#' The `find_root_file` function is a simple wrapper around
+#' [find_root()] that
+#' appends an arbitrary number of path components to the root using
+#' [base::file.path()].
+#'
+#' @param criterion A criterion, will be coerced using
+#'   [as.root_criterion()]
+#' @param path The start directory
+#' @param ... Further path components passed to [file.path()]
+#' @return The normalized path of the root as specified by the search criteria,
+#'   with the additional path components appended.
+#'   Throws an error if no root is found
+#'
+#' @examples
+#' \dontrun{
+#' find_package_root_file("tests", "testthat.R")
+#' has_file("DESCRIPTION", "^Package: ")$find_file
+#' has_file("DESCRIPTION", "^Package: ")$make_fix_file(".")
+#' }
+#'
+#' @seealso [find_root()] [utils::glob2rx()] [base::file.path()]
+#'
+#' @export
+find_root_file <- function(..., criterion, path = ".") {
+  root <- find_root(criterion = criterion, path = path)
+  file.path(root, ...)
+}
diff --git a/R/has-file.R b/R/has-file.R
new file mode 100644
index 0000000..a03251c
--- /dev/null
+++ b/R/has-file.R
@@ -0,0 +1,249 @@
+format_lines <- function(n) {
+  if (n == 1) "line" else paste0(n, " lines")
+}
+
+#' @details
+#' The `has_file` function constructs a criterion that checks for the
+#' existence of a specific file (which itself can be in a subdirectory of the
+#' root) with specific contents.
+#'
+#' @rdname root_criterion
+#' @param filepath File path (can contain directories)
+#' @param contents Regular expression to match the file contents
+#' @inheritParams base::readLines
+#' @export
+has_file <- function(filepath, contents = NULL, n = -1L) {
+  force(filepath)
+  force(contents)
+  force(n)
+
+  testfun <- eval(bquote(function(path) {
+    testfile <- file.path(path, .(filepath))
+    if (!file.exists(testfile))
+      return(FALSE)
+    if (is_dir(testfile))
+      return(FALSE)
+    match_contents(testfile, .(contents), .(n))
+  }))
+
+  desc <- paste0(
+    "contains a file `", filepath, "`",
+    if (!is.null(contents)) {
+      paste0(" with contents matching `", contents, "`",
+             if (n >= 0L) paste0(" in the first ", format_lines(n)))
+  })
+
+  root_criterion(testfun, desc)
+}
+
+#' @details
+#' The `has_dir` function constructs a criterion that checks for the
+#' existence of a specific directory.
+#'
+#' @rdname root_criterion
+#' @export
+has_dir <- function(filepath) {
+  force(filepath)
+
+  testfun <- eval(bquote(function(path) {
+    testfile <- file.path(path, .(filepath))
+    if (!file.exists(testfile))
+      return(FALSE)
+    is_dir(testfile)
+  }))
+
+  desc <- paste0("contains a directory `", filepath, "`")
+
+  root_criterion(testfun, desc)
+}
+
+#' @details
+#' The `has_file_pattern` function constructs a criterion that checks for the
+#' existence of a file that matches a pattern, with specific contents.
+#'
+#' @rdname root_criterion
+#' @param pattern Regular expression to match the file name
+#' @inheritParams base::readLines
+#' @export
+has_file_pattern <- function(pattern, contents = NULL, n = -1L) {
+  force(pattern)
+  force(contents)
+  force(n)
+
+  testfun <- eval(bquote(function(path) {
+    files <- list_files(path, .(pattern))
+    for (f in files) {
+      if (!match_contents(f, .(contents), .(n))) {
+        next
+      }
+      return(TRUE)
+    }
+    return(FALSE)
+  }))
+
+  desc <- paste0(
+    "contains a file matching `", pattern, "`",
+    if (!is.null(contents)) {
+      paste0(" with contents matching `", contents, "`",
+             if (n >= 0L) paste0(" in the first ", format_lines(n)))
+    })
+
+  root_criterion(testfun, desc)
+}
+
+#' @details
+#' The `has_dirname` function constructs a criterion that checks if the
+#' [base::dirname()] has a specific name.
+#'
+#' @rdname root_criterion
+#' @param dirname A directory name, without subdirectories
+#' @export
+has_dirname <- function(dirname, subdir = NULL) {
+  force(dirname)
+
+  testfun <- eval(bquote(function(path) {
+    dir.exists(file.path(dirname(path), .(dirname)))
+  }))
+
+  desc <- paste0("directory name is `", dirname, "`")
+
+  root_criterion(testfun, desc, subdir = subdir)
+}
+
+#' @export
+is_rstudio_project <- has_file_pattern("[.]Rproj$", contents = "^Version: ", n = 1L)
+
+#' @export
+is_r_package <- has_file("DESCRIPTION", contents = "^Package: ")
+
+#' @export
+is_remake_project <- has_file("remake.yml")
+
+#' @export
+is_projectile_project <- has_file(".projectile")
+
+#' @export
+is_git_root <- has_dir(".git")
+
+#' @export
+is_svn_root <- has_dir(".svn")
+
+#' @export
+is_vcs_root <- is_git_root | is_svn_root
+
+#' @export
+is_testthat <- has_dirname("testthat", c("tests/testthat", "testthat"))
+
+#' @export
+from_wd <- root_criterion(function(path) TRUE, "from current working directory")
+
+#' Prespecified criteria
+#'
+#' This is a collection of commonly used root criteria.
+#'
+#' @export
+criteria <- structure(
+  list(
+    is_rstudio_project = is_rstudio_project,
+    is_r_package = is_r_package,
+    is_remake_project = is_remake_project,
+    is_projectile_project = is_projectile_project,
+    is_git_root = is_git_root,
+    is_svn_root = is_svn_root,
+    is_vcs_root = is_vcs_root,
+    is_testthat = is_testthat,
+    from_wd = from_wd
+  ),
+  class = "root_criteria")
+
+#' @export
+#' @importFrom utils str
+str.root_criteria <- function(object, ...) {
+  str(lapply(object, format))
+}
+
+#' @details
+#' `is_rstudio_project` looks for a file with extension `.Rproj`.
+#'
+#' @rdname criteria
+#' @export
+"is_rstudio_project"
+
+#' @details
+#' `is_r_package` looks for a `DESCRIPTION` file.
+#'
+#' @rdname criteria
+#' @export
+"is_r_package"
+
+#' @details
+#' `is_remake_project` looks for a `remake.yml` file.
+#'
+#' @rdname criteria
+#' @export
+"is_remake_project"
+
+#' @details
+#' `is_projectile_project` looks for a `.projectile` file.
+#'
+#' @rdname criteria
+#' @export
+"is_projectile_project"
+
+#' @details
+#' `is_git_project` looks for a `.git` directory.
+#'
+#' @rdname criteria
+#' @export
+"is_git_root"
+
+#' @details
+#' `is_svn_project` looks for a `.svn` directory.
+#'
+#' @rdname criteria
+#' @export
+"is_svn_root"
+
+#' @details
+#' `is_vcs_project` looks for the root of a version control
+#' system, currently only Git and SVN are supported.
+#'
+#' @rdname criteria
+#' @export
+"is_vcs_root"
+
+#' @details
+#' `is_testthat` looks for the `testthat` directory, works when
+#'   developing, testing, and checking a package.
+#'
+#' @rdname criteria
+#' @export
+"is_testthat"
+
+#' @details
+#' `from_wd` uses the current working directory.
+#'
+#' @rdname criteria
+#' @export
+"from_wd"
+
+
+list_files <- function(path, filename) {
+  files <- dir(path = path, pattern = filename, all.files = TRUE, full.names = TRUE)
+  dirs <- is_dir(files)
+  files <- files[!dirs]
+  files
+}
+
+is_dir <- function(x) {
+  dir.exists(x)
+}
+
+match_contents <- function(f, contents, n) {
+  if (is.null(contents)) {
+    return(TRUE)
+  }
+
+  fc <- readLines(f, n)
+  any(grepl(contents, fc))
+}
diff --git a/R/root.R b/R/root.R
new file mode 100644
index 0000000..4ab40ff
--- /dev/null
+++ b/R/root.R
@@ -0,0 +1,85 @@
+#' Find the root of a directory hierarchy
+#'
+#' A \emph{root} is defined as a directory that contains a regular file
+#' whose name matches a given pattern and which optionally contains a given text.
+#' The search for a root starts at a given directory (the working directory
+#' by default), and proceeds up the directory hierarchy.
+#'
+#' Starting from the working directory, the `find_root` function searches
+#' for the root.
+#' If a root is found, the `...` arguments are used to construct a path;
+#' thus, if no extra arguments are given, the root is returned.
+#' If no root is found, an error is thrown.
+#'
+#' @inheritParams find_root_file
+#' @return The normalized path of the root as specified by the search criterion.
+#'   Throws an error if no root is found
+#'
+#' @examples
+#' \dontrun{
+#' find_root(glob2rx("DESCRIPTION"), "^Package: ")
+#' }
+#'
+#' @seealso [utils::glob2rx()] [file.path()]
+#'
+#' @export
+find_root <- function(criterion, path = ".") {
+  criterion <- as.root_criterion(criterion)
+
+  start_path <- get_start_path(path, criterion$subdir)
+  path <- start_path
+
+  for (i in seq_len(.MAX_DEPTH)) {
+    for (f in criterion$testfun) {
+      if (f(path)) {
+        return(path)
+      }
+    }
+
+    if (is_root(path)) {
+      stop("No root directory found in ", start_path, " or its parent directories. ",
+           paste(format(criterion), collapse = "\n"), call. = FALSE)
+    }
+
+    path <- dirname(path)
+  }
+
+  stop("Maximum search of ", .MAX_DEPTH, " exceeded. Last path: ", path)
+}
+
+.MAX_DEPTH <- 100L
+
+get_start_path <- function(path, subdirs) {
+  path <- normalizePath(path, winslash = "/", mustWork = TRUE)
+
+  for (subdir in subdirs) {
+    subdir_path <- file.path(path, subdir)
+    if (dir.exists(subdir_path)) {
+      return(subdir_path)
+    }
+  }
+
+  path
+}
+
+# Borrowed from devtools
+is_root <- function(path) {
+  identical(normalizePath(path, winslash = "/"),
+            normalizePath(dirname(path), winslash = "/"))
+}
+
+#' @rdname find_root
+#' @description `get_root_desc()` returns the description of the criterion
+#'   for a root path. This is especially useful for composite root criteria
+#'   created with [|.root_criterion()].
+#' @export
+get_root_desc <- function(criterion, path) {
+  for (i in seq_along(criterion$testfun)) {
+    if (criterion$testfun[[i]](path)) {
+      return(criterion$desc[[i]])
+    }
+  }
+
+  stop("path is not a root. ",
+       paste(format(criterion), collapse = "\n"), call. = FALSE)
+}
diff --git a/R/rprojroot-package.R b/R/rprojroot-package.R
new file mode 100644
index 0000000..d427a00
--- /dev/null
+++ b/R/rprojroot-package.R
@@ -0,0 +1,15 @@
+#' @details
+#' See the "Value" section in [root_criterion()] for documentation
+#' of root criterion objects, and  [criteria()] for useful predefined
+#' root criteria.
+#'
+#' @examples
+#' criteria
+#' \dontrun{
+#' is_r_package$find_file("NAMESPACE")
+#' root_fun <- is_r_package$make_fix_file()
+#' root_fun("NAMESPACE")
+#' }
+#' @import backports
+#' @api
+"_PACKAGE"
diff --git a/R/rrmake.R b/R/rrmake.R
new file mode 100644
index 0000000..127725f
--- /dev/null
+++ b/R/rrmake.R
@@ -0,0 +1,13 @@
+make_find_root_file <- function(criterion) {
+  force(criterion)
+  eval(bquote(function(..., path = ".") {
+    find_root_file(..., criterion = criterion, path = path)
+  }))
+}
+
+make_fix_root_file <- function(criterion, path) {
+  root <- find_root(criterion = criterion, path = path)
+  eval(bquote(function(...) {
+    file.path(.(root), ...)
+  }))
+}
diff --git a/R/shortcut.R b/R/shortcut.R
new file mode 100644
index 0000000..8cdb366
--- /dev/null
+++ b/R/shortcut.R
@@ -0,0 +1,15 @@
+#' @rdname find_root_file
+#' @export
+find_rstudio_root_file <- is_rstudio_project$find_file
+
+#' @rdname find_root_file
+#' @export
+find_package_root_file <- is_r_package$find_file
+
+#' @rdname find_root_file
+#' @export
+find_remake_root_file <- is_remake_project$find_file
+
+#' @rdname find_root_file
+#' @export
+find_testthat_root_file <- is_testthat$find_file
diff --git a/build/vignette.rds b/build/vignette.rds
new file mode 100644
index 0000000..2349dfe
Binary files /dev/null and b/build/vignette.rds differ
diff --git a/inst/doc/rprojroot.R b/inst/doc/rprojroot.R
new file mode 100644
index 0000000..056fb77
--- /dev/null
+++ b/inst/doc/rprojroot.R
@@ -0,0 +1,94 @@
+## ------------------------------------------------------------------------
+basename(getwd())
+
+## ------------------------------------------------------------------------
+rprojroot::is_r_package
+
+## ------------------------------------------------------------------------
+rprojroot::is_rstudio_project
+
+## ------------------------------------------------------------------------
+rprojroot::has_file(".git/index")
+
+## ------------------------------------------------------------------------
+root <- rprojroot::is_r_package
+
+## ------------------------------------------------------------------------
+readLines(root$find_file("DESCRIPTION"), 3)
+
+## ------------------------------------------------------------------------
+root_file <- root$make_fix_file()
+
+## ------------------------------------------------------------------------
+withr::with_dir(
+  "../..",
+  readLines(root_file("DESCRIPTION"), 3)
+)
+
+## ------------------------------------------------------------------------
+library(rprojroot)
+
+# List all files and directories below the root
+dir(find_root(has_file("DESCRIPTION")))
+
+# Find a file relative to the root
+file.exists(find_root_file("R", "root.R", criterion = has_file("DESCRIPTION")))
+
+## ------------------------------------------------------------------------
+has_file("DESCRIPTION")
+
+## ------------------------------------------------------------------------
+as.root_criterion("DESCRIPTION")
+
+## ------------------------------------------------------------------------
+criteria
+
+## ------------------------------------------------------------------------
+has_license <- has_file("LICENSE")
+has_license
+
+is_projecttemplate_project <- has_file("config/global.dcf", "^version: ")
+is_projecttemplate_project
+
+## ------------------------------------------------------------------------
+is_r_package | is_rstudio_project
+
+## ------------------------------------------------------------------------
+# Print first lines of the source for this document
+head(readLines(find_package_root_file("vignettes", "rprojroot.Rmd")))
+
+## ------------------------------------------------------------------------
+P <- find_package_root_file
+
+# Use a shorter alias
+file.exists(P("vignettes", "rprojroot.Rmd"))
+
+## ----error = TRUE--------------------------------------------------------
+# Use the has_license criterion to find the root
+R <- has_license$find_file
+R
+
+# Our package does not have a LICENSE file, trying to find the root results in an error
+R()
+
+## ------------------------------------------------------------------------
+# Define a function that computes file paths below the current root
+F <- is_r_package$make_fix_file()
+F
+
+# Show contents of the NAMESPACE file in our project
+readLines(F("NAMESPACE"))
+
+## ------------------------------------------------------------------------
+# Print the size of the namespace file, working directory outside the project
+withr::with_dir(
+  "../..",
+  file.size(F("NAMESPACE"))
+)
+
+## ------------------------------------------------------------------------
+is_testthat
+
+## ------------------------------------------------------------------------
+dir(is_testthat$find_file("hierarchy", path = is_r_package$find_file()))
+
diff --git a/inst/doc/rprojroot.Rmd b/inst/doc/rprojroot.Rmd
new file mode 100644
index 0000000..28d725d
--- /dev/null
+++ b/inst/doc/rprojroot.Rmd
@@ -0,0 +1,301 @@
+---
+title: "Finding files in project subdirectories"
+author: "Kirill Müller"
+date: "`r Sys.Date()`"
+output: rmarkdown::html_vignette
+vignette: >
+  %\VignetteIndexEntry{Finding files in project subdirectories}
+  %\VignetteEngine{knitr::rmarkdown}
+  %\VignetteEncoding{UTF-8}
+---
+
+The `rprojroot` package solves a seemingly trivial but annoying problem
+that occurs sooner or later
+in any largish project:
+How to find files in subdirectories?
+Ideally, file paths are relative to the *project root*.
+
+Unfortunately, we cannot always be sure about the current working directory:
+For instance, in RStudio it's sometimes:
+
+- the project root (when running R scripts),
+- a subdirectory (when building vignettes),
+- again the project root (when executing chunks of a vignette).
+
+```{r}
+basename(getwd())
+```
+
+In some cases, it's even outside the project root.
+
+This vignette starts with a very brief summary that helps you get started,
+followed by a longer description of the features.
+
+## TL;DR
+
+What is your project: An R package?
+
+```{r}
+rprojroot::is_r_package
+```
+
+Or an RStudio project?
+
+```{r}
+rprojroot::is_rstudio_project
+```
+
+Or something else?
+
+```{r}
+rprojroot::has_file(".git/index")
+```
+
+For now, we assume it's an R package:
+
+```{r}
+root <- rprojroot::is_r_package
+```
+
+The `root` object contains a function that helps locating files below the root
+of your package, regardless of your current working directory.
+If you are sure that your working directory is somewhere below your project's root,
+use the `root$find_file()` function:
+
+```{r}
+readLines(root$find_file("DESCRIPTION"), 3)
+```
+
+You can also
+construct an accessor to your root using the `root$make_fix_file()` function:
+
+```{r}
+root_file <- root$make_fix_file()
+```
+
+
+Note that `root_file()` is a *function* that works just like `$find_file()` but
+will find the files even if the current working directory is outside your project:
+
+```{r}
+withr::with_dir(
+  "../..",
+  readLines(root_file("DESCRIPTION"), 3)
+)
+```
+
+If you know the absolute path of some directory below your project,
+but cannot be sure of your current working directory,
+pass that absolute path to `root$make_fix_file()`:
+
+```r
+root_file <- root$make_fix_file("C:\\Users\\User Name\\...")
+```
+
+Get the path of standalone R scripts or vignettes
+using the `thisfile()` function in the `kimisc` package:
+
+```r
+root_file <- root$make_fix_file(dirname(kimisc::thisfile()))
+```
+
+The remainder of this vignette describes implementation details and advanced features.
+
+
+## Project root
+
+We assume a self-contained project
+where all files and directories are located below a common *root* directory.
+Also, there should be a way to unambiguously identify this root directory.
+(Often, the root contains a regular file whose name matches a given pattern,
+and/or whose contents match another pattern.)
+In this case, the following method reliably finds our project root:
+
+- Start the search in any subdirectory of our project
+- Proceed up the directory hierarchy until the root directory has been identified
+
+The Git version control system (and probably many other tools) use a similar
+approach: A Git command can be executed from within any subdirectory of a
+repository.
+
+
+### A simple example
+
+The `find_root()` function implements the core functionality.
+It returns the path to the first directory that matches the filtering criteria,
+or throws an error if there is no such directory.
+Filtering criteria are constructed in a generic fashion using the 
+`root_criterion()` function,
+the `has_file()` function constructs a criterion that checks for the presence
+of a file with a specific name and specific contents.
+
+```{r}
+library(rprojroot)
+
+# List all files and directories below the root
+dir(find_root(has_file("DESCRIPTION")))
+
+# Find a file relative to the root
+file.exists(find_root_file("R", "root.R", criterion = has_file("DESCRIPTION")))
+```
+
+Note that the following code produces identical results when building the
+vignette *and* when sourcing the chunk in RStudio,
+provided that the current working directory is the project root
+or anywhere below.
+
+
+### Criteria
+
+The `has_file()` function (and the more general `root_criterion()`)
+both return an S3 object of class `root_criterion`:
+
+```{r}
+has_file("DESCRIPTION")
+```
+
+In addition, character values are coerced to `has_file` criteria by default, this coercion is applied automatically by `find_root()`.
+(This feature is used by the introductory example.)
+
+```{r}
+as.root_criterion("DESCRIPTION")
+```
+
+The return value of these functions can be stored and reused;
+in fact, the package provides `r length(criteria)` such criteria:
+
+```{r}
+criteria
+```
+
+Defining new criteria is easy:
+
+```{r}
+has_license <- has_file("LICENSE")
+has_license
+
+is_projecttemplate_project <- has_file("config/global.dcf", "^version: ")
+is_projecttemplate_project
+```
+
+You can also combine criteria via the `|` operator:
+
+```{r}
+is_r_package | is_rstudio_project
+```
+
+
+
+### Shortcuts
+
+To avoid specifying the search criteria for the project root every time,
+shortcut functions can be created.
+The `find_package_root_file()` is a shortcut for
+`find_root_file(..., criterion = is_r_package)`:
+
+```{r}
+# Print first lines of the source for this document
+head(readLines(find_package_root_file("vignettes", "rprojroot.Rmd")))
+```
+
+To save typing effort, define a shorter alias:
+
+```{r}
+P <- find_package_root_file
+
+# Use a shorter alias
+file.exists(P("vignettes", "rprojroot.Rmd"))
+```
+
+Each criterion actually contains a function that allows finding a file below the root specified by this criterion.
+As our project does not have a file named `LICENSE`, querying the root results in an error:
+
+```{r error = TRUE}
+# Use the has_license criterion to find the root
+R <- has_license$find_file
+R
+
+# Our package does not have a LICENSE file, trying to find the root results in an error
+R()
+```
+
+
+### Fixed root
+
+We can also create a function
+that computes a path relative to the root *at creation time*.
+
+```{r}
+# Define a function that computes file paths below the current root
+F <- is_r_package$make_fix_file()
+F
+
+# Show contents of the NAMESPACE file in our project
+readLines(F("NAMESPACE"))
+```
+
+This is a more robust alternative to `$find_file()`, because it *fixes* the project
+directory when `$make_fix_file()` is called, instead of searching for it every
+time.  (For that reason it is also slightly faster, but I doubt this matters
+in practice.)
+
+This function can be used even if we later change the working directory to somewhere outside the project:
+
+```{r}
+# Print the size of the namespace file, working directory outside the project
+withr::with_dir(
+  "../..",
+  file.size(F("NAMESPACE"))
+)
+```
+
+The `make_fix_file()` member function also accepts an optional `path` argument,
+in case you know your project's root but the current working directory is somewhere outside.
+Take a look at the `thisfile()` function in the `kimisc` package for getting
+the path to the current script or `knitr` document.
+
+
+## `testthat` files
+
+Tests run with [`testthat`](https://cran.r-project.org/package=testthat)
+commonly use files that live below the `tests/testthat` directory.
+Ideally, this should work in the following situation:
+
+- During package development (working directory: package root)
+- When testing with `devtools::test()` (working directory: `tests/testthat`)
+- When running `R CMD check` (working directory: a renamed recursive copy of `tests`)
+
+The `is_testthat` criterion allows robust lookup of test files.
+
+```{r}
+is_testthat
+```
+
+The example code below lists all files in the
+[hierarchy](https://github.com/krlmlr/rprojroot/tree/master/tests/testthat/hierarchy)
+test directory.
+It uses two project root lookups in total,
+so that it also works when rendering the vignette (*sigh*):
+
+```{r}
+dir(is_testthat$find_file("hierarchy", path = is_r_package$find_file()))
+```
+
+
+## Summary
+
+The `rprojroot` package allows easy access to files below a project root
+if the project root can be identified easily, e.g. if it is the only directory
+in the whole hierarchy that contains a specific file.
+This is a robust solution for finding files in largish projects
+with a subdirectory hierarchy if the current working directory cannot be assumed
+fixed.
+(However, at least initially, the current working directory must be
+somewhere below the project root.)
+
+
+## Acknowledgement
+
+This package was inspired by the gist
+["Stop the working directory insanity"](https://gist.github.com/jennybc/362f52446fe1ebc4c49f)
+by Jennifer Bryan, and by the way Git knows where its files are.
diff --git a/inst/doc/rprojroot.html b/inst/doc/rprojroot.html
new file mode 100644
index 0000000..997af2d
--- /dev/null
+++ b/inst/doc/rprojroot.html
@@ -0,0 +1,331 @@
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+
+<meta charset="utf-8">
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="pandoc" />
+
+<meta name="viewport" content="width=device-width, initial-scale=1">
+
+<meta name="author" content="Kirill Müller" />
+
+<meta name="date" content="2017-01-16" />
+
+<title>Finding files in project subdirectories</title>
+
+
+
+<style type="text/css">code{white-space: pre;}</style>
+<style type="text/css">
+div.sourceCode { overflow-x: auto; }
+table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
+  margin: 0; padding: 0; vertical-align: baseline; border: none; }
+table.sourceCode { width: 100%; line-height: 100%; }
+td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
+td.sourceCode { padding-left: 5px; }
+code > span.kw { color: #007020; font-weight: bold; } /* Keyword */
+code > span.dt { color: #902000; } /* DataType */
+code > span.dv { color: #40a070; } /* DecVal */
+code > span.bn { color: #40a070; } /* BaseN */
+code > span.fl { color: #40a070; } /* Float */
+code > span.ch { color: #4070a0; } /* Char */
+code > span.st { color: #4070a0; } /* String */
+code > span.co { color: #60a0b0; font-style: italic; } /* Comment */
+code > span.ot { color: #007020; } /* Other */
+code > span.al { color: #ff0000; font-weight: bold; } /* Alert */
+code > span.fu { color: #06287e; } /* Function */
+code > span.er { color: #ff0000; font-weight: bold; } /* Error */
+code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
+code > span.cn { color: #880000; } /* Constant */
+code > span.sc { color: #4070a0; } /* SpecialChar */
+code > span.vs { color: #4070a0; } /* VerbatimString */
+code > span.ss { color: #bb6688; } /* SpecialString */
+code > span.im { } /* Import */
+code > span.va { color: #19177c; } /* Variable */
+code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
+code > span.op { color: #666666; } /* Operator */
+code > span.bu { } /* BuiltIn */
+code > span.ex { } /* Extension */
+code > span.pp { color: #bc7a00; } /* Preprocessor */
+code > span.at { color: #7d9029; } /* Attribute */
+code > span.do { color: #ba2121; font-style: italic; } /* Documentation */
+code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
+code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
+code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
+</style>
+
+
+
+<link href="data:text/css;charset=utf-8,body%20%7B%0Abackground%2Dcolor%3A%20%23fff%3B%0Amargin%3A%201em%20auto%3B%0Amax%2Dwidth%3A%20700px%3B%0Aoverflow%3A%20visible%3B%0Apadding%2Dleft%3A%202em%3B%0Apadding%2Dright%3A%202em%3B%0Afont%2Dfamily%3A%20%22Open%20Sans%22%2C%20%22Helvetica%20Neue%22%2C%20Helvetica%2C%20Arial%2C%20sans%2Dserif%3B%0Afont%2Dsize%3A%2014px%3B%0Aline%2Dheight%3A%201%2E35%3B%0A%7D%0A%23header%20%7B%0Atext%2Dalign%3A%20center%3B%0A%7D%0A%23TOC%20%7B%0Aclear%3A%20bot [...]
+
+</head>
+
+<body>
+
+
+
+
+<h1 class="title toc-ignore">Finding files in project subdirectories</h1>
+<h4 class="author"><em>Kirill Müller</em></h4>
+<h4 class="date"><em>2017-01-16</em></h4>
+
+
+
+<p>The <code>rprojroot</code> package solves a seemingly trivial but annoying problem that occurs sooner or later in any largish project: How to find files in subdirectories? Ideally, file paths are relative to the <em>project root</em>.</p>
+<p>Unfortunately, we cannot always be sure about the current working directory: For instance, in RStudio it’s sometimes:</p>
+<ul>
+<li>the project root (when running R scripts),</li>
+<li>a subdirectory (when building vignettes),</li>
+<li>again the project root (when executing chunks of a vignette).</li>
+</ul>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">basename</span>(<span class="kw">getwd</span>())</code></pre></div>
+<pre><code>## [1] "vignettes"</code></pre>
+<p>In some cases, it’s even outside the project root.</p>
+<p>This vignette starts with a very brief summary that helps you get started, followed by a longer description of the features.</p>
+<div id="tldr" class="section level2">
+<h2>TL;DR</h2>
+<p>What is your project: An R package?</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">rprojroot::is_r_package</code></pre></div>
+<pre><code>## Root criterion: contains a file `DESCRIPTION` with contents matching `^Package: `</code></pre>
+<p>Or an RStudio project?</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">rprojroot::is_rstudio_project</code></pre></div>
+<pre><code>## Root criterion: contains a file matching `[.]Rproj$` with contents matching `^Version: ` in the first line</code></pre>
+<p>Or something else?</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">rprojroot::<span class="kw">has_file</span>(<span class="st">".git/index"</span>)</code></pre></div>
+<pre><code>## Root criterion: contains a file `.git/index`</code></pre>
+<p>For now, we assume it’s an R package:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">root <-<span class="st"> </span>rprojroot::is_r_package</code></pre></div>
+<p>The <code>root</code> object contains a function that helps locating files below the root of your package, regardless of your current working directory. If you are sure that your working directory is somewhere below your project’s root, use the <code>root$find_file()</code> function:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">readLines</span>(root$<span class="kw">find_file</span>(<span class="st">"DESCRIPTION"</span>), <span class="dv">3</span>)</code></pre></div>
+<pre><code>## [1] "Package: rprojroot"                            
+## [2] "Title: Finding Files in Project Subdirectories"
+## [3] "Version: 1.2"</code></pre>
+<p>You can also construct an accessor to your root using the <code>root$make_fix_file()</code> function:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">root_file <-<span class="st"> </span>root$<span class="kw">make_fix_file</span>()</code></pre></div>
+<p>Note that <code>root_file()</code> is a <em>function</em> that works just like <code>$find_file()</code> but will find the files even if the current working directory is outside your project:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">withr::<span class="kw">with_dir</span>(
+  <span class="st">"../.."</span>,
+  <span class="kw">readLines</span>(<span class="kw">root_file</span>(<span class="st">"DESCRIPTION"</span>), <span class="dv">3</span>)
+)</code></pre></div>
+<pre><code>## [1] "Package: rprojroot"                            
+## [2] "Title: Finding Files in Project Subdirectories"
+## [3] "Version: 1.2"</code></pre>
+<p>If you know the absolute path of some directory below your project, but cannot be sure of your current working directory, pass that absolute path to <code>root$make_fix_file()</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">root_file <-<span class="st"> </span>root$<span class="kw">make_fix_file</span>(<span class="st">"C:</span><span class="ch">\\</span><span class="st">Users</span><span class="ch">\\</span><span class="st">User Name</span><span class="ch">\\</span><span class="st">..."</span>)</code></pre></div>
+<p>Get the path of standalone R scripts or vignettes using the <code>thisfile()</code> function in the <code>kimisc</code> package:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">root_file <-<span class="st"> </span>root$<span class="kw">make_fix_file</span>(<span class="kw">dirname</span>(kimisc::<span class="kw">thisfile</span>()))</code></pre></div>
+<p>The remainder of this vignette describes implementation details and advanced features.</p>
+</div>
+<div id="project-root" class="section level2">
+<h2>Project root</h2>
+<p>We assume a self-contained project where all files and directories are located below a common <em>root</em> directory. Also, there should be a way to unambiguously identify this root directory. (Often, the root contains a regular file whose name matches a given pattern, and/or whose contents match another pattern.) In this case, the following method reliably finds our project root:</p>
+<ul>
+<li>Start the search in any subdirectory of our project</li>
+<li>Proceed up the directory hierarchy until the root directory has been identified</li>
+</ul>
+<p>The Git version control system (and probably many other tools) use a similar approach: A Git command can be executed from within any subdirectory of a repository.</p>
+<div id="a-simple-example" class="section level3">
+<h3>A simple example</h3>
+<p>The <code>find_root()</code> function implements the core functionality. It returns the path to the first directory that matches the filtering criteria, or throws an error if there is no such directory. Filtering criteria are constructed in a generic fashion using the <code>root_criterion()</code> function, the <code>has_file()</code> function constructs a criterion that checks for the presence of a file with a specific name and specific contents.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">library</span>(rprojroot)
+
+<span class="co"># List all files and directories below the root</span>
+<span class="kw">dir</span>(<span class="kw">find_root</span>(<span class="kw">has_file</span>(<span class="st">"DESCRIPTION"</span>)))</code></pre></div>
+<pre><code>##  [1] "API"              "DESCRIPTION"      "Makefile"        
+##  [4] "NAMESPACE"        "NEWS.md"          "R"               
+##  [7] "README.md"        "_pkgdown.yml"     "appveyor.yml"    
+## [10] "cran-comments.md" "docs"             "inst"            
+## [13] "man"              "readme"           "revdep"          
+## [16] "rprojroot.Rproj"  "tests"            "tic.R"           
+## [19] "vignettes"</code></pre>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="co"># Find a file relative to the root</span>
+<span class="kw">file.exists</span>(<span class="kw">find_root_file</span>(<span class="st">"R"</span>, <span class="st">"root.R"</span>, <span class="dt">criterion =</span> <span class="kw">has_file</span>(<span class="st">"DESCRIPTION"</span>)))</code></pre></div>
+<pre><code>## [1] TRUE</code></pre>
+<p>Note that the following code produces identical results when building the vignette <em>and</em> when sourcing the chunk in RStudio, provided that the current working directory is the project root or anywhere below.</p>
+</div>
+<div id="criteria" class="section level3">
+<h3>Criteria</h3>
+<p>The <code>has_file()</code> function (and the more general <code>root_criterion()</code>) both return an S3 object of class <code>root_criterion</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">has_file</span>(<span class="st">"DESCRIPTION"</span>)</code></pre></div>
+<pre><code>## Root criterion: contains a file `DESCRIPTION`</code></pre>
+<p>In addition, character values are coerced to <code>has_file</code> criteria by default, this coercion is applied automatically by <code>find_root()</code>. (This feature is used by the introductory example.)</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">as.root_criterion</span>(<span class="st">"DESCRIPTION"</span>)</code></pre></div>
+<pre><code>## Root criterion: contains a file `DESCRIPTION`</code></pre>
+<p>The return value of these functions can be stored and reused; in fact, the package provides 9 such criteria:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">criteria</code></pre></div>
+<pre><code>## $is_rstudio_project
+## Root criterion: contains a file matching `[.]Rproj$` with contents matching `^Version: ` in the first line
+## 
+## $is_r_package
+## Root criterion: contains a file `DESCRIPTION` with contents matching `^Package: `
+## 
+## $is_remake_project
+## Root criterion: contains a file `remake.yml`
+## 
+## $is_projectile_project
+## Root criterion: contains a file `.projectile`
+## 
+## $is_git_root
+## Root criterion: contains a directory `.git`
+## 
+## $is_svn_root
+## Root criterion: contains a directory `.svn`
+## 
+## $is_vcs_root
+## Root criterion: one of
+## - contains a directory `.git`
+## - contains a directory `.svn`
+## 
+## $is_testthat
+## Root criterion: directory name is `testthat` (also look in subdirectories: `tests/testthat`, `testthat`)
+## 
+## $from_wd
+## Root criterion: from current working directory
+## 
+## attr(,"class")
+## [1] "root_criteria"</code></pre>
+<p>Defining new criteria is easy:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">has_license <-<span class="st"> </span><span class="kw">has_file</span>(<span class="st">"LICENSE"</span>)
+has_license</code></pre></div>
+<pre><code>## Root criterion: contains a file `LICENSE`</code></pre>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">is_projecttemplate_project <-<span class="st"> </span><span class="kw">has_file</span>(<span class="st">"config/global.dcf"</span>, <span class="st">"^version: "</span>)
+is_projecttemplate_project</code></pre></div>
+<pre><code>## Root criterion: contains a file `config/global.dcf` with contents matching `^version: `</code></pre>
+<p>You can also combine criteria via the <code>|</code> operator:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">is_r_package |<span class="st"> </span>is_rstudio_project</code></pre></div>
+<pre><code>## Root criterion: one of
+## - contains a file `DESCRIPTION` with contents matching `^Package: `
+## - contains a file matching `[.]Rproj$` with contents matching `^Version: ` in the first line</code></pre>
+</div>
+<div id="shortcuts" class="section level3">
+<h3>Shortcuts</h3>
+<p>To avoid specifying the search criteria for the project root every time, shortcut functions can be created. The <code>find_package_root_file()</code> is a shortcut for <code>find_root_file(..., criterion = is_r_package)</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="co"># Print first lines of the source for this document</span>
+<span class="kw">head</span>(<span class="kw">readLines</span>(<span class="kw">find_package_root_file</span>(<span class="st">"vignettes"</span>, <span class="st">"rprojroot.Rmd"</span>)))</code></pre></div>
+<pre><code>## [1] "---"                                               
+## [2] "title: \"Finding files in project subdirectories\""
+## [3] "author: \"Kirill Müller\""                         
+## [4] "date: \"`r Sys.Date()`\""                          
+## [5] "output: rmarkdown::html_vignette"                  
+## [6] "vignette: >"</code></pre>
+<p>To save typing effort, define a shorter alias:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">P <-<span class="st"> </span>find_package_root_file
+
+<span class="co"># Use a shorter alias</span>
+<span class="kw">file.exists</span>(<span class="kw">P</span>(<span class="st">"vignettes"</span>, <span class="st">"rprojroot.Rmd"</span>))</code></pre></div>
+<pre><code>## [1] TRUE</code></pre>
+<p>Each criterion actually contains a function that allows finding a file below the root specified by this criterion. As our project does not have a file named <code>LICENSE</code>, querying the root results in an error:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="co"># Use the has_license criterion to find the root</span>
+R <-<span class="st"> </span>has_license$find_file
+R</code></pre></div>
+<pre><code>## function (..., path = ".") 
+## {
+##     find_root_file(..., criterion = criterion, path = path)
+## }
+## <environment: 0x55ca0ef0cf10></code></pre>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="co"># Our package does not have a LICENSE file, trying to find the root results in an error</span>
+<span class="kw">R</span>()</code></pre></div>
+<pre><code>## Error: No root directory found in /tmp/Rtmp76Lko7/Rbuild36db711fd839/rprojroot/vignettes or its parent directories. Root criterion: contains a file `LICENSE`</code></pre>
+</div>
+<div id="fixed-root" class="section level3">
+<h3>Fixed root</h3>
+<p>We can also create a function that computes a path relative to the root <em>at creation time</em>.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="co"># Define a function that computes file paths below the current root</span>
+F <-<span class="st"> </span>is_r_package$<span class="kw">make_fix_file</span>()
+F</code></pre></div>
+<pre><code>## function (...) 
+## {
+##     file.path("/tmp/Rtmp76Lko7/Rbuild36db711fd839/rprojroot", 
+##         ...)
+## }
+## <environment: 0x55ca0cd18718></code></pre>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="co"># Show contents of the NAMESPACE file in our project</span>
+<span class="kw">readLines</span>(<span class="kw">F</span>(<span class="st">"NAMESPACE"</span>))</code></pre></div>
+<pre><code>##  [1] "# Generated by roxygen2: do not edit by hand"
+##  [2] ""                                            
+##  [3] "S3method(\"|\",root_criterion)"              
+##  [4] "S3method(as.root_criterion,character)"       
+##  [5] "S3method(as.root_criterion,default)"         
+##  [6] "S3method(as.root_criterion,root_criterion)"  
+##  [7] "S3method(format,root_criterion)"             
+##  [8] "S3method(print,root_criterion)"              
+##  [9] "S3method(str,root_criteria)"                 
+## [10] "export(as.root_criterion)"                   
+## [11] "export(criteria)"                            
+## [12] "export(find_package_root_file)"              
+## [13] "export(find_remake_root_file)"               
+## [14] "export(find_root)"                           
+## [15] "export(find_root_file)"                      
+## [16] "export(find_rstudio_root_file)"              
+## [17] "export(find_testthat_root_file)"             
+## [18] "export(from_wd)"                             
+## [19] "export(get_root_desc)"                       
+## [20] "export(has_dir)"                             
+## [21] "export(has_dirname)"                         
+## [22] "export(has_file)"                            
+## [23] "export(has_file_pattern)"                    
+## [24] "export(is.root_criterion)"                   
+## [25] "export(is_git_root)"                         
+## [26] "export(is_projectile_project)"               
+## [27] "export(is_r_package)"                        
+## [28] "export(is_remake_project)"                   
+## [29] "export(is_rstudio_project)"                  
+## [30] "export(is_svn_root)"                         
+## [31] "export(is_testthat)"                         
+## [32] "export(is_vcs_root)"                         
+## [33] "export(root_criterion)"                      
+## [34] "import(backports)"                           
+## [35] "importFrom(utils,str)"</code></pre>
+<p>This is a more robust alternative to <code>$find_file()</code>, because it <em>fixes</em> the project directory when <code>$make_fix_file()</code> is called, instead of searching for it every time. (For that reason it is also slightly faster, but I doubt this matters in practice.)</p>
+<p>This function can be used even if we later change the working directory to somewhere outside the project:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="co"># Print the size of the namespace file, working directory outside the project</span>
+withr::<span class="kw">with_dir</span>(
+  <span class="st">"../.."</span>,
+  <span class="kw">file.size</span>(<span class="kw">F</span>(<span class="st">"NAMESPACE"</span>))
+)</code></pre></div>
+<pre><code>## [1] 880</code></pre>
+<p>The <code>make_fix_file()</code> member function also accepts an optional <code>path</code> argument, in case you know your project’s root but the current working directory is somewhere outside. Take a look at the <code>thisfile()</code> function in the <code>kimisc</code> package for getting the path to the current script or <code>knitr</code> document.</p>
+</div>
+</div>
+<div id="testthat-files" class="section level2">
+<h2><code>testthat</code> files</h2>
+<p>Tests run with <a href="https://cran.r-project.org/package=testthat"><code>testthat</code></a> commonly use files that live below the <code>tests/testthat</code> directory. Ideally, this should work in the following situation:</p>
+<ul>
+<li>During package development (working directory: package root)</li>
+<li>When testing with <code>devtools::test()</code> (working directory: <code>tests/testthat</code>)</li>
+<li>When running <code>R CMD check</code> (working directory: a renamed recursive copy of <code>tests</code>)</li>
+</ul>
+<p>The <code>is_testthat</code> criterion allows robust lookup of test files.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">is_testthat</code></pre></div>
+<pre><code>## Root criterion: directory name is `testthat` (also look in subdirectories: `tests/testthat`, `testthat`)</code></pre>
+<p>The example code below lists all files in the <a href="https://github.com/krlmlr/rprojroot/tree/master/tests/testthat/hierarchy">hierarchy</a> test directory. It uses two project root lookups in total, so that it also works when rendering the vignette (<em>sigh</em>):</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">dir</span>(is_testthat$<span class="kw">find_file</span>(<span class="st">"hierarchy"</span>, <span class="dt">path =</span> is_r_package$<span class="kw">find_file</span>()))</code></pre></div>
+<pre><code>## [1] "DESCRIPTION"     "a"               "b"               "c"              
+## [5] "hierarchy.Rproj"</code></pre>
+</div>
+<div id="summary" class="section level2">
+<h2>Summary</h2>
+<p>The <code>rprojroot</code> package allows easy access to files below a project root if the project root can be identified easily, e.g. if it is the only directory in the whole hierarchy that contains a specific file. This is a robust solution for finding files in largish projects with a subdirectory hierarchy if the current working directory cannot be assumed fixed. (However, at least initially, the current working directory must be somewhere below the project root.)</p>
+</div>
+<div id="acknowledgement" class="section level2">
+<h2>Acknowledgement</h2>
+<p>This package was inspired by the gist <a href="https://gist.github.com/jennybc/362f52446fe1ebc4c49f">“Stop the working directory insanity”</a> by Jennifer Bryan, and by the way Git knows where its files are.</p>
+</div>
+
+
+
+<!-- dynamically load mathjax for compatibility with self-contained -->
+<script>
+  (function () {
+    var script = document.createElement("script");
+    script.type = "text/javascript";
+    script.src  = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
+    document.getElementsByTagName("head")[0].appendChild(script);
+  })();
+</script>
+
+</body>
+</html>
diff --git a/man/criteria.Rd b/man/criteria.Rd
new file mode 100644
index 0000000..d8ad0ef
--- /dev/null
+++ b/man/criteria.Rd
@@ -0,0 +1,62 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/has-file.R
+\docType{data}
+\name{criteria}
+\alias{criteria}
+\alias{is_rstudio_project}
+\alias{is_r_package}
+\alias{is_remake_project}
+\alias{is_projectile_project}
+\alias{is_git_root}
+\alias{is_svn_root}
+\alias{is_vcs_root}
+\alias{is_testthat}
+\alias{from_wd}
+\title{Prespecified criteria}
+\format{An object of class \code{root_criteria} of length 9.}
+\usage{
+criteria
+
+is_rstudio_project
+
+is_r_package
+
+is_remake_project
+
+is_projectile_project
+
+is_git_root
+
+is_svn_root
+
+is_vcs_root
+
+is_testthat
+
+from_wd
+}
+\description{
+This is a collection of commonly used root criteria.
+}
+\details{
+\code{is_rstudio_project} looks for a file with extension \code{.Rproj}.
+
+\code{is_r_package} looks for a \code{DESCRIPTION} file.
+
+\code{is_remake_project} looks for a \code{remake.yml} file.
+
+\code{is_projectile_project} looks for a \code{.projectile} file.
+
+\code{is_git_project} looks for a \code{.git} directory.
+
+\code{is_svn_project} looks for a \code{.svn} directory.
+
+\code{is_vcs_project} looks for the root of a version control
+system, currently only Git and SVN are supported.
+
+\code{is_testthat} looks for the \code{testthat} directory, works when
+developing, testing, and checking a package.
+
+\code{from_wd} uses the current working directory.
+}
+\keyword{datasets}
diff --git a/man/find_root.Rd b/man/find_root.Rd
new file mode 100644
index 0000000..9f2c085
--- /dev/null
+++ b/man/find_root.Rd
@@ -0,0 +1,47 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/root.R
+\name{find_root}
+\alias{find_root}
+\alias{get_root_desc}
+\title{Find the root of a directory hierarchy}
+\usage{
+find_root(criterion, path = ".")
+
+get_root_desc(criterion, path)
+}
+\arguments{
+\item{criterion}{A criterion, will be coerced using
+\code{\link[=as.root_criterion]{as.root_criterion()}}}
+
+\item{path}{The start directory}
+}
+\value{
+The normalized path of the root as specified by the search criterion.
+Throws an error if no root is found
+}
+\description{
+A \emph{root} is defined as a directory that contains a regular file
+whose name matches a given pattern and which optionally contains a given text.
+The search for a root starts at a given directory (the working directory
+by default), and proceeds up the directory hierarchy.
+
+\code{get_root_desc()} returns the description of the criterion
+for a root path. This is especially useful for composite root criteria
+created with \code{\link[=|.root_criterion]{|.root_criterion()}}.
+}
+\details{
+Starting from the working directory, the \code{find_root} function searches
+for the root.
+If a root is found, the \code{...} arguments are used to construct a path;
+thus, if no extra arguments are given, the root is returned.
+If no root is found, an error is thrown.
+}
+\examples{
+\dontrun{
+find_root(glob2rx("DESCRIPTION"), "^Package: ")
+}
+
+}
+\seealso{
+\code{\link[utils:glob2rx]{utils::glob2rx()}} \code{\link[=file.path]{file.path()}}
+}
diff --git a/man/find_root_file.Rd b/man/find_root_file.Rd
new file mode 100644
index 0000000..c97d51e
--- /dev/null
+++ b/man/find_root_file.Rd
@@ -0,0 +1,54 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/file.R, R/shortcut.R
+\name{find_root_file}
+\alias{find_root_file}
+\alias{find_rstudio_root_file}
+\alias{find_package_root_file}
+\alias{find_remake_root_file}
+\alias{find_testthat_root_file}
+\title{File paths relative to the root of a directory hierarchy}
+\usage{
+find_root_file(..., criterion, path = ".")
+
+find_rstudio_root_file(..., path = ".")
+
+find_package_root_file(..., path = ".")
+
+find_remake_root_file(..., path = ".")
+
+find_testthat_root_file(..., path = ".")
+}
+\arguments{
+\item{...}{Further path components passed to \code{\link[=file.path]{file.path()}}}
+
+\item{criterion}{A criterion, will be coerced using
+\code{\link[=as.root_criterion]{as.root_criterion()}}}
+
+\item{path}{The start directory}
+}
+\value{
+The normalized path of the root as specified by the search criteria,
+with the additional path components appended.
+Throws an error if no root is found
+}
+\description{
+Append an arbitrary number of path components to the root using
+\code{\link[base:file.path]{base::file.path()}}.
+}
+\details{
+The \code{find_root_file} function is a simple wrapper around
+\code{\link[=find_root]{find_root()}} that
+appends an arbitrary number of path components to the root using
+\code{\link[base:file.path]{base::file.path()}}.
+}
+\examples{
+\dontrun{
+find_package_root_file("tests", "testthat.R")
+has_file("DESCRIPTION", "^Package: ")$find_file
+has_file("DESCRIPTION", "^Package: ")$make_fix_file(".")
+}
+
+}
+\seealso{
+\code{\link[=find_root]{find_root()}} \code{\link[utils:glob2rx]{utils::glob2rx()}} \code{\link[base:file.path]{base::file.path()}}
+}
diff --git a/man/root_criterion.Rd b/man/root_criterion.Rd
new file mode 100644
index 0000000..d47d910
--- /dev/null
+++ b/man/root_criterion.Rd
@@ -0,0 +1,120 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/criterion.R, R/has-file.R
+\name{root_criterion}
+\alias{root_criterion}
+\alias{is.root_criterion}
+\alias{as.root_criterion}
+\alias{as.root_criterion.character}
+\alias{as.root_criterion.root_criterion}
+\alias{|.root_criterion}
+\alias{has_file}
+\alias{has_dir}
+\alias{has_file_pattern}
+\alias{has_dirname}
+\title{Is a directory the project root?}
+\usage{
+root_criterion(testfun, desc, subdir = NULL)
+
+is.root_criterion(x)
+
+as.root_criterion(x)
+
+\method{as.root_criterion}{character}(x)
+
+\method{as.root_criterion}{root_criterion}(x)
+
+\method{|}{root_criterion}(x, y)
+
+has_file(filepath, contents = NULL, n = -1L)
+
+has_dir(filepath)
+
+has_file_pattern(pattern, contents = NULL, n = -1L)
+
+has_dirname(dirname, subdir = NULL)
+}
+\arguments{
+\item{testfun}{A function with one parameter that returns \code{TRUE}
+if the directory specified by this parameter is the project root,
+and \code{FALSE} otherwise. Can also be a list of such functions.}
+
+\item{desc}{A textual description of the test criterion, of the same length
+as \code{testfun}}
+
+\item{subdir}{Subdirectories to start the search in, if found}
+
+\item{x}{An object}
+
+\item{y}{An object}
+
+\item{filepath}{File path (can contain directories)}
+
+\item{contents}{Regular expression to match the file contents}
+
+\item{n}{integer.  The (maximal) number of lines to
+    read.  Negative values indicate that one should read up to the end of
+    input on the connection.}
+
+\item{pattern}{Regular expression to match the file name}
+
+\item{dirname}{A directory name, without subdirectories}
+}
+\value{
+An S3 object of class \code{root_criterion} wit the following members:
+
+\describe{
+\item{\code{testfun}}{The \code{testfun} argument}
+\item{\code{desc}}{The \code{desc} argument}
+\item{\code{subdir}}{The \code{subdir} argument}
+\item{\code{find_file}}{A function with \code{...} argument that returns
+for a path relative to the root specified by this criterion.
+The optional \code{path} argument specifies the starting directory,
+which defaults to \code{"."}.
+}
+\item{\code{make_fix_file}}{A function with a \code{path} argument that
+returns a function that finds paths relative to the root.  For a
+criterion \code{cr}, the result of \code{cr$make_fix_file(".")(...)}
+is identical to \code{cr$find_file(...)}. The function created by
+\code{make_fix_file} can be saved to a variable to be more independent
+of the current working directory.
+}
+}
+}
+\description{
+Objects of the \code{root_criterion} class decide if a
+given directory is a project root.
+}
+\details{
+Construct criteria using \code{root_criterion} in a very general fashion
+by specifying a function with a \code{path} argument, and a description.
+
+The \code{as.root_criterion} function accepts objects of class
+\code{root_criterion}, and character values; the latter will be
+converted to criteria using \code{has_file}.
+
+Root criteria can be combined with the \code{|} operator. The result is a
+composite root criterion that requires either of the original criteria to
+match.
+
+The \code{has_file} function constructs a criterion that checks for the
+existence of a specific file (which itself can be in a subdirectory of the
+root) with specific contents.
+
+The \code{has_dir} function constructs a criterion that checks for the
+existence of a specific directory.
+
+The \code{has_file_pattern} function constructs a criterion that checks for the
+existence of a file that matches a pattern, with specific contents.
+
+The \code{has_dirname} function constructs a criterion that checks if the
+\code{\link[base:dirname]{base::dirname()}} has a specific name.
+}
+\examples{
+root_criterion(function(path) file.exists(file.path(path, "somefile")), "has somefile")
+has_file("DESCRIPTION")
+is_r_package
+is_r_package$find_file
+\dontrun{
+is_r_package$make_fix_file(".")
+}
+}
diff --git a/man/rprojroot-package.Rd b/man/rprojroot-package.Rd
new file mode 100644
index 0000000..4090994
--- /dev/null
+++ b/man/rprojroot-package.Rd
@@ -0,0 +1,39 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/rprojroot-package.R
+\docType{package}
+\name{rprojroot-package}
+\alias{rprojroot}
+\alias{rprojroot-package}
+\title{rprojroot: Finding Files in Project Subdirectories}
+\description{
+Robust, reliable and flexible paths to files below a
+project root. The 'root' of a project is defined as a directory
+that matches a certain criterion, e.g., it contains a certain
+regular file.
+}
+\details{
+See the "Value" section in \code{\link[=root_criterion]{root_criterion()}} for documentation
+of root criterion objects, and  \code{\link[=criteria]{criteria()}} for useful predefined
+root criteria.
+}
+\examples{
+criteria
+\dontrun{
+is_r_package$find_file("NAMESPACE")
+root_fun <- is_r_package$make_fix_file()
+root_fun("NAMESPACE")
+}
+}
+\seealso{
+Useful links:
+\itemize{
+  \item \url{https://github.com/krlmlr/rprojroot}
+  \item \url{https://krlmlr.github.io/rprojroot}
+  \item Report bugs at \url{https://github.com/krlmlr/rprojroot/issues}
+}
+
+}
+\author{
+\strong{Maintainer}: Kirill Müller \email{krlmlr+r at mailbox.org}
+
+}
diff --git a/tests/testthat.R b/tests/testthat.R
new file mode 100644
index 0000000..49fa1b6
--- /dev/null
+++ b/tests/testthat.R
@@ -0,0 +1,4 @@
+library(testthat)
+library(rprojroot)
+
+test_check("rprojroot")
diff --git a/tests/testthat/hierarchy/DESCRIPTION b/tests/testthat/hierarchy/DESCRIPTION
new file mode 100644
index 0000000..110eb5a
--- /dev/null
+++ b/tests/testthat/hierarchy/DESCRIPTION
@@ -0,0 +1,9 @@
+Package: hierarchy
+Title: What the Package Does (one line, title case)
+Version: 0.0-0
+Authors at R: as.person("Kirill Müller <krlmlr+r at mailbox.org> [aut, cre]")
+Description: What the package does (one paragraph)
+Depends: R (>= 3.2.0)
+License: GPL-3
+LazyData: true
+Encoding: UTF-8
diff --git a/tests/testthat/hierarchy/a/b/a b/tests/testthat/hierarchy/a/b/a
new file mode 100644
index 0000000..e69de29
diff --git a/tests/testthat/hierarchy/a/b/b b/tests/testthat/hierarchy/a/b/b
new file mode 100644
index 0000000..6821c18
--- /dev/null
+++ b/tests/testthat/hierarchy/a/b/b
@@ -0,0 +1 @@
+File b
diff --git a/tests/testthat/hierarchy/a/b/c/d b/tests/testthat/hierarchy/a/b/c/d
new file mode 100644
index 0000000..e69de29
diff --git a/tests/testthat/hierarchy/a/remake.yml b/tests/testthat/hierarchy/a/remake.yml
new file mode 100644
index 0000000..e69de29
diff --git a/tests/testthat/hierarchy/b b/tests/testthat/hierarchy/b
new file mode 100644
index 0000000..a51ca34
--- /dev/null
+++ b/tests/testthat/hierarchy/b
@@ -0,0 +1 @@
+File b in root
diff --git a/tests/testthat/hierarchy/c b/tests/testthat/hierarchy/c
new file mode 100644
index 0000000..e69de29
diff --git a/tests/testthat/hierarchy/hierarchy.Rproj b/tests/testthat/hierarchy/hierarchy.Rproj
new file mode 100644
index 0000000..30e02be
--- /dev/null
+++ b/tests/testthat/hierarchy/hierarchy.Rproj
@@ -0,0 +1,21 @@
+Version: 1.0
+
+RestoreWorkspace: No
+SaveWorkspace: No
+AlwaysSaveHistory: Default
+
+EnableCodeIndexing: Yes
+UseSpacesForTab: Yes
+NumSpacesForTab: 2
+Encoding: UTF-8
+
+RnwWeave: knitr
+LaTeX: pdfLaTeX
+
+AutoAppendNewline: Yes
+StripTrailingWhitespace: Yes
+
+BuildType: Package
+PackageUseDevtools: Yes
+PackageInstallArgs: --no-multiarch --with-keep.source
+PackageRoxygenize: rd,collate,namespace
diff --git a/tests/testthat/package/DESCRIPTION b/tests/testthat/package/DESCRIPTION
new file mode 100644
index 0000000..e69de29
diff --git a/tests/testthat/package/tests/testthat.R b/tests/testthat/package/tests/testthat.R
new file mode 100644
index 0000000..e69de29
diff --git a/tests/testthat/package/tests/testthat/test-something.R b/tests/testthat/package/tests/testthat/test-something.R
new file mode 100644
index 0000000..e69de29
diff --git a/tests/testthat/test-criterion.R b/tests/testthat/test-criterion.R
new file mode 100644
index 0000000..dc5dca8
--- /dev/null
+++ b/tests/testthat/test-criterion.R
@@ -0,0 +1,49 @@
+context("criterion")
+
+test_that("root_criterion", {
+  expect_error(root_criterion(5, "Bogus"), "must have exactly one argument")
+  expect_error(root_criterion(identity, "Bogus"), "must have exactly one argument")
+  expect_true(is.root_criterion(root_criterion(function(path) FALSE, "Never")))
+})
+
+test_that("is.root_criterion", {
+  expect_true(is.root_criterion(has_file("DESCRIPTION")))
+  expect_false(is.root_criterion("DESCRIPTION"))
+  expect_true(is.root_criterion(as.root_criterion("DESCRIPTION")))
+  expect_equal(as.root_criterion("x"), has_file("x"))
+  expect_error(as.root_criterion(5), "Cannot coerce")
+})
+
+test_that("Absolute paths are returned", {
+  expect_equal(find_root("testthat.R"),
+               normalizePath(find_root("testthat.R"), winslash = "/"))
+})
+
+test_that("Formatting", {
+  expect_match(paste(format(is_r_package), collapse = "\n"),
+               "^Root criterion: .*DESCRIPTION")
+  expect_output(print(is_r_package), "^Root criterion: .*DESCRIPTION")
+  expect_output(print(is_vcs_root), "^Root criterion: one of\n- .*[.]git.*\n- .*[.]svn")
+})
+
+test_that("Formatting criteria", {
+  ret <- character()
+  with_mock(
+    `base::cat` = function(..., sep = "") ret <<- c(ret, paste(..., sep = sep)),
+     str(criteria)
+  )
+  expect_match(ret[[1]], "^List of ")
+})
+
+test_that("Combining criteria", {
+  skip_on_cran()
+
+  comb_crit <- is_r_package | is_rstudio_project
+
+  expect_true(is.root_criterion(comb_crit))
+
+  expect_match(paste0(format(comb_crit), collapse = "\n"), "\n- .*\n- ")
+
+  expect_equal(find_root(comb_crit, "hierarchy"),
+               find_root(is_rstudio_project, "hierarchy/a"))
+})
diff --git a/tests/testthat/test-make.R b/tests/testthat/test-make.R
new file mode 100644
index 0000000..f990838
--- /dev/null
+++ b/tests/testthat/test-make.R
@@ -0,0 +1,13 @@
+context("make")
+
+test_that("Shortcuts", {
+  expect_equal(make_find_root_file("testthat.R")("testthat"),
+               normalizePath(getwd(), winslash = "/"))
+
+  R <- make_fix_root_file("testthat.R", getwd())
+  oldwd <- setwd("~")
+  on.exit(setwd(oldwd))
+
+  expect_equal(normalizePath(R("testthat"), mustWork = TRUE),
+               normalizePath(oldwd, mustWork = TRUE))
+})
diff --git a/tests/testthat/test-root.R b/tests/testthat/test-root.R
new file mode 100644
index 0000000..061ec8f
--- /dev/null
+++ b/tests/testthat/test-root.R
@@ -0,0 +1,197 @@
+context("root")
+
+test_that("has_file", {
+  wd <- normalizePath(getwd(), winslash = "/")
+  hierarchy <- function(n = 0L) {
+    do.call(file.path, list(wd, "hierarchy", "a", "b", "c")[seq_len(n + 1L)])
+  }
+
+  stop_path <- hierarchy(1L)
+  path <- hierarchy(4L)
+
+  with_mock(
+    `rprojroot:::is_root` = function(x) x == stop_path,
+    expect_equal(find_root("a", path = path), hierarchy(3L)),
+    expect_equal(find_root("b", path = path), hierarchy(3L)),
+    expect_equal(find_root("b/a", path = path), hierarchy(2L)),
+    expect_equal(find_root_file("c", criterion = "b/a", path = path),
+                 file.path(hierarchy(2L), "c")),
+    expect_equal(find_root("c", path = path), hierarchy(1L)),
+    expect_equal(find_root("d", path = path), hierarchy(4L)),
+    expect_equal(find_root(has_file("DESCRIPTION", "^Package: ", 1), path = path), hierarchy(1L)),
+    expect_equal(find_root(has_file("DESCRIPTION", "^Package: "), path = path), hierarchy(1L)),
+    expect_error(find_root("test-root.R", path = path),
+                 "No root directory found.* file `.*`"),
+    expect_error(find_root("rprojroot.Rproj", path = path),
+                 "No root directory found.* file `.*`"),
+    expect_error(find_root(has_file("e", "f"), path = path),
+                 "No root directory found.* file `.*` with contents"),
+    expect_error(find_root(has_file("e", "f", 1), path = path),
+                 "No root directory found.* file `.*` with contents .* in the first line")
+  )
+})
+
+test_that("has_file_pattern", {
+  wd <- normalizePath(getwd(), winslash = "/")
+  hierarchy <- function(n = 0L) {
+    do.call(file.path, list(wd, "hierarchy", "a", "b", "c")[seq_len(n + 1L)])
+  }
+
+  stop_path <- hierarchy(1L)
+  path <- hierarchy(4L)
+
+  with_mock(
+    `rprojroot:::is_root` = function(x) x == stop_path,
+    expect_equal(find_root(has_file_pattern(glob2rx("a")), path = path), hierarchy(3L)),
+    expect_equal(find_root(has_file_pattern(glob2rx("b")), path = path), hierarchy(3L)),
+    expect_equal(find_root(has_file_pattern("[ab]", "File b"), path = path),
+                 hierarchy(3L)),
+    expect_equal(find_root(has_file_pattern("[ab]", "File b in root"), path = path),
+                 hierarchy(1L)),
+    expect_equal(find_root(has_file_pattern(glob2rx("c")), path = path), hierarchy(1L)),
+    expect_equal(find_root(has_file_pattern(glob2rx("d")), path = path), hierarchy(4L)),
+    expect_equal(find_root(has_file_pattern(glob2rx("DESCRIPTION"), "^Package: ", 1), path = path), hierarchy(1L)),
+    expect_equal(find_root(has_file_pattern(glob2rx("DESCRIPTION"), "^Package: "), path = path), hierarchy(1L)),
+    expect_error(find_root(has_file_pattern(glob2rx("test-root.R")), path = path),
+                 "No root directory found.* file matching "),
+    expect_error(find_root(has_file_pattern(glob2rx("rprojroot.Rproj")), path = path),
+                 "No root directory found.* file matching "),
+    expect_error(find_root(has_file_pattern(glob2rx("e"), "f"), path = path),
+                 "No root directory found.* with contents"),
+    expect_error(find_root(has_file_pattern(glob2rx("e"), "f", 1), path = path),
+                 "No root directory found.* with contents .* in the first line")
+  )
+})
+
+test_that("has_dir", {
+  wd <- normalizePath(getwd(), winslash = "/")
+  hierarchy <- function(n = 0L) {
+    do.call(file.path, list(wd, "hierarchy", "a", "b", "c")[seq_len(n + 1L)])
+  }
+
+  stop_path <- hierarchy(1L)
+  path <- hierarchy(4L)
+
+  with_mock(
+    `rprojroot:::is_root` = function(x) x == stop_path,
+    expect_equal(find_root(has_dir("a"), path = path), hierarchy(1L)),
+    expect_equal(find_root(has_dir("b"), path = path), hierarchy(2L)),
+    expect_equal(find_root_file("c", criterion = has_dir("b"), path = path),
+                 file.path(hierarchy(2L), "c")),
+    expect_equal(find_root(has_dir("c"), path = path), hierarchy(3L)),
+    expect_error(find_root(has_dir("d"), path = path),
+                 "No root directory found.* a directory `.*`"),
+    expect_error(find_root(has_dir("rprojroot.Rproj"), path = path),
+                 "No root directory found.* a directory `.*`"),
+    TRUE
+  )
+})
+
+test_that("has_dirname", {
+  wd <- normalizePath(getwd(), winslash = "/")
+  hierarchy <- function(n = 0L) {
+    do.call(file.path, list(wd, "hierarchy", "a", "b", "c")[seq_len(n + 1L)])
+  }
+
+  stop_path <- hierarchy(1L)
+  path <- hierarchy(4L)
+
+  with_mock(
+    `rprojroot:::is_root` = function(x) x == stop_path,
+    expect_equal(find_root(has_dirname("a"), path = path), hierarchy(2L)),
+    expect_equal(find_root(has_dirname("b"), path = path), hierarchy(3L)),
+    expect_equal(find_root_file("c", criterion = has_dirname("b"), path = path),
+                 file.path(hierarchy(3L), "c")),
+    expect_equal(find_root(has_dirname("c"), path = path), hierarchy(4L)),
+    expect_error(find_root(has_dirname("d"), path = path),
+                 "No root directory found.* is `.*`"),
+    expect_error(find_root(has_dirname("rprojroot.Rproj"), path = path),
+                 "No root directory found.* is `.*`"),
+    TRUE
+  )
+})
+
+test_that("concrete criteria", {
+  wd <- normalizePath(getwd(), winslash = "/")
+  hierarchy <- function(n = 0L) {
+    do.call(file.path, list(wd, "hierarchy", "a", "b", "c")[seq_len(n + 1L)])
+  }
+
+  # HACK
+  writeLines(character(), file.path(hierarchy(3L), ".projectile"))
+
+  stop_path <- hierarchy(0L)
+  path <- hierarchy(4L)
+
+  with_mock(
+    `rprojroot:::is_root` = function(x) x == stop_path,
+    expect_equal(find_root(is_rstudio_project, path = path), hierarchy(1L)),
+    expect_equal(find_root(is_remake_project, path = path), hierarchy(2L)),
+    expect_equal(find_root(is_projectile_project, path = path), hierarchy(3L)),
+    TRUE
+  )
+})
+
+test_that("is_svn_root", {
+  temp_dir <- tempfile("svn")
+  unzip("vcs/svn.zip", exdir = temp_dir)
+  wd <- normalizePath(temp_dir, winslash = "/")
+
+  hierarchy <- function(n = 0L) {
+    do.call(file.path, list(wd, "svn", "a", "b", "c")[seq_len(n + 1L)])
+  }
+
+  stop_path <- normalizePath(tempdir(), winslash = "/")
+  path <- hierarchy(4L)
+
+  with_mock(
+    `rprojroot:::is_root` = function(x) x == stop_path,
+    expect_equal(find_root(is_svn_root, path = path), hierarchy(1L)),
+    expect_equal(find_root(is_vcs_root, path = path), hierarchy(1L)),
+    expect_error(find_root(is_svn_root, path = hierarchy(0L)),
+                 "No root directory found.* a directory `.*`"),
+    expect_error(find_root(is_vcs_root, path = hierarchy(0L)),
+                 "No root directory found.* a directory `.*`"),
+    TRUE
+  )
+})
+
+test_that("is_git_root", {
+  temp_dir <- tempfile("git")
+  unzip("vcs/git.zip", exdir = temp_dir)
+  wd <- normalizePath(temp_dir, winslash = "/")
+
+  hierarchy <- function(n = 0L) {
+    do.call(file.path, list(wd, "git", "a", "b", "c")[seq_len(n + 1L)])
+  }
+
+  stop_path <- normalizePath(tempdir(), winslash = "/")
+  path <- hierarchy(4L)
+
+  with_mock(
+    `rprojroot:::is_root` = function(x) x == stop_path,
+    expect_equal(find_root(is_git_root, path = path), hierarchy(1L)),
+    expect_equal(find_root(is_vcs_root, path = path), hierarchy(1L)),
+    expect_error(find_root(is_git_root, path = hierarchy(0L)),
+                 "No root directory found.* a directory `.*`"),
+    expect_error(find_root(is_vcs_root, path = hierarchy(0L)),
+                 "No root directory found.* a directory `.*`"),
+    TRUE
+  )
+})
+
+test_that("finds root", {
+  skip_on_cran()
+  # Checks that search for root actually terminates
+  expect_error(find_root("/"), "No root directory found.* file `.*`")
+})
+
+test_that("stops if depth reached", {
+  find_root_mocked <- find_root
+  mock_env <- new.env()
+  mock_env$dirname <- identity
+  environment(find_root_mocked) <- mock_env
+
+  # Checks that search for root terminates for very deep hierarchies
+  expect_error(find_root_mocked(""), "Maximum search of [0-9]+ exceeded")
+})
diff --git a/tests/testthat/test-testthat.R b/tests/testthat/test-testthat.R
new file mode 100644
index 0000000..087c47a
--- /dev/null
+++ b/tests/testthat/test-testthat.R
@@ -0,0 +1,17 @@
+context("testthat")
+
+test_that("is_testthat", {
+  expect_match(paste(format(is_testthat), collapse = "\n"),
+               "^.*directory name is `testthat` .* subdirectories.*`tests/testthat`.*`testthat`.*$")
+  expect_match(paste(format(is_testthat), collapse = "\n"),
+               "directory name is `testthat` .* subdirectories.*`tests/testthat`.*`testthat`.*")
+
+  testthat_path <- normalizePath("package/tests/testthat", winslash = "/")
+  expect_equal(is_testthat$find_file(path = "package"), testthat_path)
+  expect_equal(is_testthat$find_file(path = "package/tests"), testthat_path)
+  expect_equal(is_testthat$find_file(path = "package/tests/testthat"), testthat_path)
+})
+
+test_that("dogfood", {
+  expect_true(file.exists(is_testthat$find_file("hierarchy", "a", "b", "c", "d")))
+})
diff --git a/tests/testthat/vcs/git.zip b/tests/testthat/vcs/git.zip
new file mode 100644
index 0000000..1b3ca57
Binary files /dev/null and b/tests/testthat/vcs/git.zip differ
diff --git a/tests/testthat/vcs/svn.zip b/tests/testthat/vcs/svn.zip
new file mode 100644
index 0000000..0f3fbd7
Binary files /dev/null and b/tests/testthat/vcs/svn.zip differ
diff --git a/vignettes/rprojroot.Rmd b/vignettes/rprojroot.Rmd
new file mode 100644
index 0000000..28d725d
--- /dev/null
+++ b/vignettes/rprojroot.Rmd
@@ -0,0 +1,301 @@
+---
+title: "Finding files in project subdirectories"
+author: "Kirill Müller"
+date: "`r Sys.Date()`"
+output: rmarkdown::html_vignette
+vignette: >
+  %\VignetteIndexEntry{Finding files in project subdirectories}
+  %\VignetteEngine{knitr::rmarkdown}
+  %\VignetteEncoding{UTF-8}
+---
+
+The `rprojroot` package solves a seemingly trivial but annoying problem
+that occurs sooner or later
+in any largish project:
+How to find files in subdirectories?
+Ideally, file paths are relative to the *project root*.
+
+Unfortunately, we cannot always be sure about the current working directory:
+For instance, in RStudio it's sometimes:
+
+- the project root (when running R scripts),
+- a subdirectory (when building vignettes),
+- again the project root (when executing chunks of a vignette).
+
+```{r}
+basename(getwd())
+```
+
+In some cases, it's even outside the project root.
+
+This vignette starts with a very brief summary that helps you get started,
+followed by a longer description of the features.
+
+## TL;DR
+
+What is your project: An R package?
+
+```{r}
+rprojroot::is_r_package
+```
+
+Or an RStudio project?
+
+```{r}
+rprojroot::is_rstudio_project
+```
+
+Or something else?
+
+```{r}
+rprojroot::has_file(".git/index")
+```
+
+For now, we assume it's an R package:
+
+```{r}
+root <- rprojroot::is_r_package
+```
+
+The `root` object contains a function that helps locating files below the root
+of your package, regardless of your current working directory.
+If you are sure that your working directory is somewhere below your project's root,
+use the `root$find_file()` function:
+
+```{r}
+readLines(root$find_file("DESCRIPTION"), 3)
+```
+
+You can also
+construct an accessor to your root using the `root$make_fix_file()` function:
+
+```{r}
+root_file <- root$make_fix_file()
+```
+
+
+Note that `root_file()` is a *function* that works just like `$find_file()` but
+will find the files even if the current working directory is outside your project:
+
+```{r}
+withr::with_dir(
+  "../..",
+  readLines(root_file("DESCRIPTION"), 3)
+)
+```
+
+If you know the absolute path of some directory below your project,
+but cannot be sure of your current working directory,
+pass that absolute path to `root$make_fix_file()`:
+
+```r
+root_file <- root$make_fix_file("C:\\Users\\User Name\\...")
+```
+
+Get the path of standalone R scripts or vignettes
+using the `thisfile()` function in the `kimisc` package:
+
+```r
+root_file <- root$make_fix_file(dirname(kimisc::thisfile()))
+```
+
+The remainder of this vignette describes implementation details and advanced features.
+
+
+## Project root
+
+We assume a self-contained project
+where all files and directories are located below a common *root* directory.
+Also, there should be a way to unambiguously identify this root directory.
+(Often, the root contains a regular file whose name matches a given pattern,
+and/or whose contents match another pattern.)
+In this case, the following method reliably finds our project root:
+
+- Start the search in any subdirectory of our project
+- Proceed up the directory hierarchy until the root directory has been identified
+
+The Git version control system (and probably many other tools) use a similar
+approach: A Git command can be executed from within any subdirectory of a
+repository.
+
+
+### A simple example
+
+The `find_root()` function implements the core functionality.
+It returns the path to the first directory that matches the filtering criteria,
+or throws an error if there is no such directory.
+Filtering criteria are constructed in a generic fashion using the 
+`root_criterion()` function,
+the `has_file()` function constructs a criterion that checks for the presence
+of a file with a specific name and specific contents.
+
+```{r}
+library(rprojroot)
+
+# List all files and directories below the root
+dir(find_root(has_file("DESCRIPTION")))
+
+# Find a file relative to the root
+file.exists(find_root_file("R", "root.R", criterion = has_file("DESCRIPTION")))
+```
+
+Note that the following code produces identical results when building the
+vignette *and* when sourcing the chunk in RStudio,
+provided that the current working directory is the project root
+or anywhere below.
+
+
+### Criteria
+
+The `has_file()` function (and the more general `root_criterion()`)
+both return an S3 object of class `root_criterion`:
+
+```{r}
+has_file("DESCRIPTION")
+```
+
+In addition, character values are coerced to `has_file` criteria by default, this coercion is applied automatically by `find_root()`.
+(This feature is used by the introductory example.)
+
+```{r}
+as.root_criterion("DESCRIPTION")
+```
+
+The return value of these functions can be stored and reused;
+in fact, the package provides `r length(criteria)` such criteria:
+
+```{r}
+criteria
+```
+
+Defining new criteria is easy:
+
+```{r}
+has_license <- has_file("LICENSE")
+has_license
+
+is_projecttemplate_project <- has_file("config/global.dcf", "^version: ")
+is_projecttemplate_project
+```
+
+You can also combine criteria via the `|` operator:
+
+```{r}
+is_r_package | is_rstudio_project
+```
+
+
+
+### Shortcuts
+
+To avoid specifying the search criteria for the project root every time,
+shortcut functions can be created.
+The `find_package_root_file()` is a shortcut for
+`find_root_file(..., criterion = is_r_package)`:
+
+```{r}
+# Print first lines of the source for this document
+head(readLines(find_package_root_file("vignettes", "rprojroot.Rmd")))
+```
+
+To save typing effort, define a shorter alias:
+
+```{r}
+P <- find_package_root_file
+
+# Use a shorter alias
+file.exists(P("vignettes", "rprojroot.Rmd"))
+```
+
+Each criterion actually contains a function that allows finding a file below the root specified by this criterion.
+As our project does not have a file named `LICENSE`, querying the root results in an error:
+
+```{r error = TRUE}
+# Use the has_license criterion to find the root
+R <- has_license$find_file
+R
+
+# Our package does not have a LICENSE file, trying to find the root results in an error
+R()
+```
+
+
+### Fixed root
+
+We can also create a function
+that computes a path relative to the root *at creation time*.
+
+```{r}
+# Define a function that computes file paths below the current root
+F <- is_r_package$make_fix_file()
+F
+
+# Show contents of the NAMESPACE file in our project
+readLines(F("NAMESPACE"))
+```
+
+This is a more robust alternative to `$find_file()`, because it *fixes* the project
+directory when `$make_fix_file()` is called, instead of searching for it every
+time.  (For that reason it is also slightly faster, but I doubt this matters
+in practice.)
+
+This function can be used even if we later change the working directory to somewhere outside the project:
+
+```{r}
+# Print the size of the namespace file, working directory outside the project
+withr::with_dir(
+  "../..",
+  file.size(F("NAMESPACE"))
+)
+```
+
+The `make_fix_file()` member function also accepts an optional `path` argument,
+in case you know your project's root but the current working directory is somewhere outside.
+Take a look at the `thisfile()` function in the `kimisc` package for getting
+the path to the current script or `knitr` document.
+
+
+## `testthat` files
+
+Tests run with [`testthat`](https://cran.r-project.org/package=testthat)
+commonly use files that live below the `tests/testthat` directory.
+Ideally, this should work in the following situation:
+
+- During package development (working directory: package root)
+- When testing with `devtools::test()` (working directory: `tests/testthat`)
+- When running `R CMD check` (working directory: a renamed recursive copy of `tests`)
+
+The `is_testthat` criterion allows robust lookup of test files.
+
+```{r}
+is_testthat
+```
+
+The example code below lists all files in the
+[hierarchy](https://github.com/krlmlr/rprojroot/tree/master/tests/testthat/hierarchy)
+test directory.
+It uses two project root lookups in total,
+so that it also works when rendering the vignette (*sigh*):
+
+```{r}
+dir(is_testthat$find_file("hierarchy", path = is_r_package$find_file()))
+```
+
+
+## Summary
+
+The `rprojroot` package allows easy access to files below a project root
+if the project root can be identified easily, e.g. if it is the only directory
+in the whole hierarchy that contains a specific file.
+This is a robust solution for finding files in largish projects
+with a subdirectory hierarchy if the current working directory cannot be assumed
+fixed.
+(However, at least initially, the current working directory must be
+somewhere below the project root.)
+
+
+## Acknowledgement
+
+This package was inspired by the gist
+["Stop the working directory insanity"](https://gist.github.com/jennybc/362f52446fe1ebc4c49f)
+by Jennifer Bryan, and by the way Git knows where its files are.

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/r-cran-rprojroot.git



More information about the debian-med-commit mailing list