Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

160 fs #195

Merged
merged 13 commits into from Jul 4, 2023
Merged
5 changes: 3 additions & 2 deletions DESCRIPTION
Expand Up @@ -21,7 +21,7 @@ Depends:
R (>= 3.6)
Imports:
cli,
fs,
fs (>= 1.6.2),
fuj (>= 0.1.1),
magrittr (>= 2.0.1),
stats (>= 3.6),
Expand All @@ -43,7 +43,8 @@ Suggests:
testthat (>= 3.0.0),
tibble (>= 3.0.4),
waldo (>= 0.2.5),
withr (>= 2.3.0)
withr (>= 2.3.0),
xopen
RoxygenNote: 7.2.3
Roxygen: list(markdown = TRUE)
Config/testthat/edition: 3
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
@@ -1,5 +1,6 @@
# mark (development version)

* adds greater use of `{fs}` over base file functions [#160](https://github.com/jmbarbone/mark/issues/160)
* improvements in `todos()` and `fixmes()`
* File extension can now be set [#170](https://github.com/jmbarbone/mark/issues/170), which by default includes `qmd` ([#163]((https://github.com/jmbarbone/mark/issues/163))) and `py` files
* new parameter `ignore` to ignore any files
Expand Down
139 changes: 61 additions & 78 deletions R/directory.R
Expand Up @@ -118,8 +118,8 @@ remove_temp_files <- function(x) {
norm_path <- function(x = ".", check = FALSE, remove = check) {
stopifnot(is.character(x))

paths <- normalizePath(x, winslash = .Platform$file.sep, mustWork = FALSE)
ind <- !file.exists(paths)
paths <- fs::path_norm(x)
ind <- !fs::file_exists(paths)

if (check && any(ind)) {
warning(cond_norm_path_found(paths[ind]))
Expand All @@ -135,14 +135,14 @@ norm_path <- function(x = ".", check = FALSE, remove = check) {
#' @export
#' @rdname norm_path
file_path <- function(..., check = FALSE, remove = check) {
fp <- file.path(..., fsep = .Platform$file.sep)
norm_path(fp, check = check, remove = remove)
norm_path(fs::path(...), check = check, remove = remove)
}

#' @export
#' @rdname norm_path
user_file <- function(..., check = FALSE, remove = check) {
file_path(Sys.getenv("R_USER"), ..., check = check, remove = remove)
r_user <- norm_path(Sys.getenv("R_USER"), check = TRUE)
file_path(r_user, ..., check = check, remove = remove)
}

#' File information utils
Expand Down Expand Up @@ -203,17 +203,17 @@ smallest_file <- function(x) {
#'
#' Opens the given files(s)
#'
#' @details
#' `open_file` is an alternative to `shell.exec()` that can take take
#' multiple files.
#' `list_files` and `list_dirs` are mostly wrappers for [base::list.files()] and
#' [base::list.dirs()] with preferred defaults and pattern searching on the
#' full file path.
#' @details `open_file` is an alternative to `shell.exec()` that can take take
#' multiple files. `list_files` and `list_dirs` are mostly wrappers for
#' [fs::dir_ls()] with preferred defaults and pattern searching on the full file
#' path.
#'
#' `file_open` is simply an alias.
#'
#' @inheritParams norm_path
#' @inheritParams base::list.files
#' @inheritParams fs::dir_ls
#' @param pattern,glob Pattern to search for files. `glob` is absorbed into
#' `pattern`, through [utils::glob2rx()].
#' @param ignore_case logical. Should pattern-matching be case-insensitive?
#' @param all a logical value. If FALSE, only the names of visible files are
#' returned (following Unix-style visibility, that is files whose name does
Expand All @@ -223,12 +223,15 @@ smallest_file <- function(x) {
#' @param negate Logical, if `TRUE` will inversely select files that do not
#' match the provided pattern
#'
#' @export
#' @return
#' * `open_file()`, `shell_exec()`: A logical vector where `TRUE` successfully
#' opened, `FALSE` did not and `NA` did not try to open (file not found)
#' opened, `FALSE` did not and `NA` did not try to open (file not found)
#' * `list_files()`, `list_dirs()`: A vector of full paths
#' @name file_utils
NULL

#' @export
#' @rdname file_utils
open_file <- function(x) {
x <- norm_path(x, check = TRUE)
out <- rep(NA, length(x))
Expand All @@ -243,67 +246,62 @@ file_open <- open_file
#' @rdname file_utils
#' @export
shell_exec <- function(x) {
open_fun <- switch(
Sys.info()[["sysname"]],
Windows = shell.exec, # nolint: object_usage_linter.
Linux = function(file) system2("xdg-open", shQuote(file, "sh")),
Darwin = function(file) system2("xdg-open", shQuote(file, "sh")),
stop(cond_shell_exec(Sys.info()[["sysname"]]))
)
if (is_windows()) {
open_fun <- function(path) shell.exec(file = path) # nolint: object_usage_linter, line_length_linter.
} else {
require_namespace("xopen")
open_fun <- function(path) xopen::xopen(target = path)
}

open_fun <- match.fun(open_fun)
x <- norm_path(x, check = TRUE)

FUN <- function(file) { # nolint: object_name_linter.
do_open_fun <- function(file) {
inherits(try(open_fun(x), silent = TRUE), "try-error")
}

invisible(!vap_lgl(x, FUN))
invisible(!vap_lgl(x, do_open_fun))
}

#' @rdname file_utils
#' @export
list_files <- function(
x = ".",
pattern = NULL,
pattern = utils::glob2rx(glob),
glob = NULL,
ignore_case = FALSE,
all = FALSE,
negate = FALSE,
basename = FALSE
) {

pattern <- force(pattern) %|||% NULL
path <- norm_path(x, check = TRUE)

if (length(path) == 1L && is.na(path)) {
return(NA_character_)
}

files <- if (basename && !negate) {
# default behavior
list.files(
path = path,
pattern = pattern,
all.files = all,
full.names = TRUE,
recursive = all,
ignore.case = ignore_case,
include.dirs = FALSE,
no.. = TRUE
)
} else {
# If we want the regular expression applied to the entire file
# Or if we want to negate the expression
list.files(
path = path,
pattern = NULL,
all.files = all,
full.names = TRUE,
recursive = all,
ignore.case = FALSE,
include.dirs = FALSE,
no.. = FALSE
)
}
files <-
if (basename) {
# default behavior
fs::dir_ls(
path = path,
regexp = pattern,
all = all,
recurse = all,
ignore.case = ignore_case,
invert = negate,
type = "file"
)
} else {
# If we want the regular expression applied to the entire file
fs::dir_ls(
path = path,
all = all,
recurse = all,
type = "file"
)
}

files <- norm_path(files)
files <- files[is_file(files)]
Expand Down Expand Up @@ -346,13 +344,8 @@ list_dirs <- function(
return(NA_character_)
}

dirs <- norm_path(
list.dirs(
path = path,
full.names = TRUE,
recursive = all
)
)
dirs <- fs::dir_ls(path = path, type = "directory", recurse = TRUE)
dirs <- norm_path(dirs)

if (is.null(pattern)) {
return(dirs)
Expand Down Expand Up @@ -398,7 +391,7 @@ is_dir <- function(x) {
#' @export
is_file <- function(x) {
stopifnot(!no_length(x), is.character(x))
isdir <- file.info(x, extra_cols = FALSE)$isdir
isdir <- file.info(x, extra_cols = FALSE)[["isdir"]]
!is.na(isdir) & !isdir
}

Expand All @@ -410,26 +403,23 @@ file_create <- function(x, overwrite = FALSE) {
}

if (overwrite) {
file.remove(x[is_file(x)])
fs::file_delete(x[is_file(x)])
}

invisible(file.create(x, showWarnings = TRUE))
invisible(fs::file_create(x))
}

dir_create <- function(x, overwrite = FALSE) {
if (overwrite) {
e <- is_dir(x)

if (any(e)) {
for (i in x[e]) {
if (dir.exists(i)) {
unlink(i, recursive = TRUE)
}
e <- which(is_dir(x))
for (i in e) {
if (fs::dir_exists(i)) {
fs::dir_delete(i)
}
}
}

invisible(dir.create(x, showWarnings = TRUE, recursive = TRUE))
invisible(fs::dir_create(x))
}

#' File name
Expand Down Expand Up @@ -474,13 +464,13 @@ add_file_timestamp <- function(
}

bn <- file_name(x)
ext <- tools::file_ext(x)
ext <- fs::path_ext(x)

if (length(sep) > 1) {
sep <- collapse0(sep)
}

file.path(dirname(x), paste0(bn, sep, ts, if (ext != "") ".", ext))
fs::path(dirname(x), paste0(bn, sep, ts, if (ext != "") ".", ext))
}

# conditions --------------------------------------------------------------
Expand All @@ -497,13 +487,6 @@ cond_norm_path_found <- function(paths) {
)
}

cond_shell_exec <- function(x) {
new_condition(
paste0("sysname not recognized:", toString(x)),
"shell_exec_sysname"
)
}

cond_file_create_dir <- function(x) {
new_condition(
paste0(
Expand Down
4 changes: 2 additions & 2 deletions R/sourcing.R
Expand Up @@ -31,7 +31,7 @@ ksource <- function(file, ..., quiet = TRUE, cd = FALSE, env = parent.frame()) {
require_namespace("knitr")
stopifnot(is.environment(env))
o <- mark_temp("R")
on.exit(file.remove(o), add = TRUE)
on.exit(fs::file_delete(o), add = TRUE)
source(knitr::purl(file, output = o, quiet = quiet), chdir = cd, local = env)
}

Expand Down Expand Up @@ -136,7 +136,7 @@ eval_named_chunk <- function(rmd_file, label_name) {
#' @name source_files

source_r_dir <- function(dir, echo = FALSE, quiet = FALSE, ...) {
files <- list.files(dir, pattern = "\\.[rR]$", full.names = TRUE)
files <- fs::dir_ls(dir, regexp = "\\.[rR]$")
invisible(lapply(sort(files), source_r_file, q = quiet, ...))
}

Expand Down
11 changes: 3 additions & 8 deletions R/todos.R
Expand Up @@ -100,25 +100,20 @@ do_todo <- function( # nolint: cyclocomp_linter.
stop(cond_do_todo_path())
}

stopifnot(file.exists(path), length(text) == 1L)
stopifnot(fs::file_exists(path), length(text) == 1L)

ls <- list(...)

if (is_dir(path)) {
if (
!has_char(path) ||
!(force || any(tolower(tools::file_ext(list.files(path))) == "rproj"))
!(force || any(tolower(tools::file_ext(fs::dir_ls(path))) == "rproj"))
) {
message("Did not search for TODOS in ", norm_path(path))
return(invisible(NULL))
}

files <- list.files(
path,
recursive = TRUE,
ignore.case = TRUE,
full.names = TRUE
)
files <- fs::dir_ls(path, recurse = TRUE, ignore.case = TRUE)

if (!is.null(ext)) {
files <- files[tolower(tools::file_ext(files)) %in% tolower(ext)]
Expand Down
14 changes: 7 additions & 7 deletions man/file_utils.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/reexports.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions tests/testthat/test-directory.R
@@ -1,8 +1,8 @@

test_that("tests with temp dir", {
expect_equal_path <- function(x, y) {
x_short <- file.path(basename(dirname(x)), basename(x))
y_short <- file.path(basename(dirname(y)), basename(y))
x_short <- fs::path(basename(dirname(x)), basename(x))
y_short <- fs::path(basename(dirname(y)), basename(y))

expect_true(file.exists(y), info = "Expected path does not exist")
expect_equal(x_short, y_short)
Expand Down