here icon indicating copy to clipboard operation
here copied to clipboard

use_here()

Open krlmlr opened this issue 3 years ago • 2 comments

A helper function that adds here::i_am() calls to all files in a project or to the active file.

  • If a DESCRIPTION is present, files in the R/ subdirectory should be ignored
  • Create a uuid argument with uuid::UUIDgenerate()
  • Add to the first line in an R script or to the first line of the first chunk
  • If a call to here::i_am() is found, does it need to be moved to the top?
  • Only suggest cli and other packages, no new strong dependencies

krlmlr avatar Nov 15 '20 09:11 krlmlr

Here is my attempt :

use_here <- function(){
    # get the working directory 
    # used here::: just to test
    wd <- here:::.root_env$root$wd
    # get the R(md) files in the folder tree
    files <- list.files(wd, pattern="\\.R(md)?$", ignore.case=TRUE, recursive=TRUE)
    # if it's an R package remove the files in the R/ directory
    # and the ones in the test directory
    is_pkg <- tryCatch({is_r_package$make_fix_file(); T}, error= function(...) F)
    if(is_pkg){
        files <- files[!grepl("^(R|tests|vignettes)", files)]
    } 
    # get the files that were modified
    files <- sapply(files, prepend_i_am)
    # print them
    cat(unlist(files), sep="\n")
}
prepend_i_am <- function(x, is_rmarkdown = grepl("\\.Rmd$", x, ignore.case = TRUE)){
    # read the lines
    lines <- readLines(x)
    # check if a here::i_am call is made
    has_i_am <- grepl("here::i_am\\(.+?\\)", lines)
    # generate the i_am call
    here_string <- sprintf('here::i_am("%s", uuid = "%s")', x, uuid::UUIDgenerate())
    if(any(has_i_am)){
        # get the line of the first call
        which(has_i_am)[1] -> line
        # if it's already in the first line return
        if(line==1) return()
        # grab the here_string defined by the user might not contain a uuid
        here_string <- sub("(here::i_am\\(.+?\\) *;?)", "\\1", lines[line])
        # remove the call
        lines[line] <- sub(here_string, "", lines[line], fixed=T)
    }
    if(is_rmarkdown){
        # get the first code chunk
        add_here <- grep("``` *\\{ *r.+?\\}", lines, ignore.case=TRUE)[1] 
        # if it's already on the first line bail
        if(any(has_i_am) && line==add_here+1) return()
        # add it directly after the opening of the chunk
        lines <- c(lines[1:add_here], here_string, lines[-(1:add_here)])
    }else{
        # add it to the first line
        lines <- c(here_string, lines)    
    }
    # write the modified lines
    writeLines(lines, con = x)
    x
}

what does this code do:

  • all of the requirements
  • this code ignores files in the tests/ subdirectory as well as those in the R/ if the project is an R package.

@krlmlr thoughts?

moutikabdessabour avatar Dec 25 '20 02:12 moutikabdessabour

Thanks for your contribution, it looks very useful. A few suggestions:

  • Expand T and F
  • Replace 1:x with seq_len()
  • Replace sapply() with vapply() or map_chr()
  • Run styler on this code.

Would you like to submit a PR? I'll take this when I'll next work on {here}.

krlmlr avatar Apr 02 '21 04:04 krlmlr