diff --git a/DESCRIPTION b/DESCRIPTION index 6b3ecb9..c31ef61 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,14 +1,15 @@ Package: here Title: A Simpler Way to Find Your Files -Version: 0.0-5 +Version: 0.0-6 Authors@R: person("Kirill", "Müller", role = c("aut", "cre"), email = "krlmlr+r@mailbox.org") Description: Constructs paths to your project's files. -Imports: rprojroot (>= 1.1) +Imports: rprojroot (>= 1.2) License: GPL-3 Encoding: UTF-8 LazyData: true -Date: 2016-10-29 +Date: 2017-01-16 URL: https://github.com/krlmlr/here, http://krlmlr.github.io/here BugReports: https://github.com/krlmlr/here/issues Roxygen: list(markdown = TRUE) RoxygenNote: 5.0.1.9000 +Remotes: krlmlr/rprojroot@r-1.2 diff --git a/NAMESPACE b/NAMESPACE index e73cd6b..fba6d01 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,4 +1,6 @@ # Generated by roxygen2: do not edit by hand +export(dr_here) export(here) +export(set_here) import(rprojroot) diff --git a/NEWS.md b/NEWS.md index 09f339e..395d5dc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,11 @@ +## here 0.0-6 (2017-01-16) + +- New `set_here()` function that creates a `.here` file and talks about it by default (#1). +- New `dr_here()`, describes why `here()` has settled for a particular path. +- Recognize projectile projects and VCS roots. +- Using working directory as fallback produces wrong results, reverted. + + ## here 0.0-5 (2016-10-29) - `remake` projects are also recognized by default. diff --git a/R/here.R b/R/here.R index dcae654..119bef9 100644 --- a/R/here.R +++ b/R/here.R @@ -1,28 +1,98 @@ #' Find your files #' -#' Uses a reasonable heuristics to find your project's files, based on the -#' current working directory when the package is loaded. +#' `here()` uses a reasonable heuristics to find your project's files, based on +#' the current working directory at the time when the package is loaded. +#' Use it as a drop-in replacement for [file.path()], it will always locate the +#' files relative to your project root. +#' +#' This package is intended for interactive use only. #' Use [rprojroot::has_file()] or the other functions in -#' the \pkg{rprojroot} package for more control. +#' the \pkg{rprojroot} package for more control, +#' or for package development. +#' +#' @evalRd format_root_section() #' #' @param ... \code{[character]}\cr #' Path components below the project root, can be empty. #' @export #' @examples #' here() +#' \dontrun{here("some/path/below/your/project/root.txt")} here <- function(...) { .root_env$f(...) } +#' @rdname here +#' @description `dr_here()` shows a message that by default also includes the +#' reason why `here()` is set to a particular directory. Use this function +#' if `here()` gives unexpected results. +#' @param show_reason \code{[logical(1)]}\cr +#' Include reason in output of `dr_here()`, defaults to `TRUE`. +#' @export +dr_here <- function(show_reason = TRUE) { + message(format_dr_here(show_reason = show_reason)) +} + +format_dr_here <- function(show_reason) { + root <- .root_env$f() + paste0( + "here() starts at ", root, + if (show_reason) { + paste0(", because it ", get_root_desc(.root_env$crit, root)) + } + ) +} + +#' @rdname here +#' @description `set_here()` creates an empty file named `.here`, by default +#' in the current directory. When `here` encounters such a file, it uses the +#' directory that contains this file as root. This is useful if none of the +#' default criteria apply. +#' @param path \code{[character(1)]}\cr +#' Directory where to create `.here` file, defaults to the current directory. +#' @param verbose \code{[logical(1)]}\cr +#' Verbose output, defaults to `TRUE`. +#' @export +set_here <- function(path = ".", verbose = TRUE) { + path <- normalizePath(path) + file_path <- file.path(path, ".here") + + if (file.exists(file_path)) { + if (verbose) { + message("File .here already exists in ", path) + } + } else { + writeLines(character(), file_path) + if (verbose) { + message("Created file .here in ", path) + } + } + + invisible(file_path) +} + +is_here <- has_file(".here") + .root_env <- new.env(parent = emptyenv()) #' @import rprojroot .onLoad <- function(libname, pkgname) { - crit <- is_rstudio_project | is_r_package | is_remake_project | from_wd - - .root_env$f <- crit$make_fix_file() + .root_env$crit <- is_here | is_rstudio_project | is_r_package | is_remake_project | is_projectile_project | is_vcs_root + .root_env$f <- .root_env$crit$make_fix_file() } .onAttach <- function(libname, pkgname) { - packageStartupMessage("here() starts at ", .root_env$f()) + packageStartupMessage(format_dr_here(show_reason = FALSE)) +} + +format_root_section <- function() { + paste( + "\\section{Project root}{", + "Starting with the current working directory during package load time, `here` will walk the directory hierarchy upwards until it finds a directory that satisfies at least one of the following conditions:", + paste(format(.root_env$crit)[-1], collapse = "\n"), + "", + "Once established, the root directory doesn't change during the active R session. `here()` then appends the arguments to the root directory.", + "}", + sep = "\n" + ) } diff --git a/man/here.Rd b/man/here.Rd index d6ae8e7..819edcf 100644 --- a/man/here.Rd +++ b/man/here.Rd @@ -2,21 +2,65 @@ % Please edit documentation in R/here.R \name{here} \alias{here} +\alias{dr_here} +\alias{set_here} \title{Find your files} \usage{ here(...) + +dr_here(show_reason = TRUE) + +set_here(path = ".", verbose = TRUE) } \arguments{ \item{...}{\code{[character]}\cr Path components below the project root, can be empty.} + +\item{show_reason}{\code{[logical(1)]}\cr +Include reason in output of \code{dr_here()}, defaults to \code{TRUE}.} + +\item{path}{\code{[character(1)]}\cr +Directory where to create \code{.here} file, defaults to the current directory.} + +\item{verbose}{\code{[logical(1)]}\cr +Verbose output, defaults to \code{TRUE}.} } \description{ -Uses a reasonable heuristics to find your project's files, based on the -current working directory when the package is loaded. +\code{here()} uses a reasonable heuristics to find your project's files, based on +the current working directory at the time when the package is loaded. +Use it as a drop-in replacement for \code{\link[=file.path]{file.path()}}, it will always locate the +files relative to your project root. + +\code{dr_here()} shows a message that by default also includes the +reason why \code{here()} is set to a particular directory. Use this function +if \code{here()} gives unexpected results. + +\code{set_here()} creates an empty file named \code{.here}, by default +in the current directory. When \code{here} encounters such a file, it uses the +directory that contains this file as root. This is useful if none of the +default criteria apply. +} +\details{ +This package is intended for interactive use only. Use \code{\link[rprojroot:has_file]{rprojroot::has_file()}} or the other functions in -the \pkg{rprojroot} package for more control. +the \pkg{rprojroot} package for more control, +or for package development. } \examples{ here() +\dontrun{here("some/path/below/your/project/root.txt")} +} +\section{Project root}{ +Starting with the current working directory during package load time, \code{here} will walk the directory hierarchy upwards until it finds a directory that satisfies at least one of the following conditions: +\itemize{ +\item contains a file \code{.here} +\item contains a file matching \code{[.]Rproj$} with contents matching \code{^Version:} in the first line +\item contains a file \code{DESCRIPTION} with contents matching \code{^Package:} +\item contains a file \code{remake.yml} +\item contains a file \code{.projectile} +\item contains a directory \code{.git} +\item contains a directory \code{.svn} } +Once established, the root directory doesn't change during the active R session. \code{here()} then appends the arguments to the root directory. +}