| Title: | Search, Inspect, and Replace Text Across Files |
|---|---|
| Description: | An inspectable and composable workflow for search-and-replace in text files. Files can be listed, filtered, and searched separately, with inspectable exclusions showing what was excluded and why. Matches are represented as structured vectors that can be printed with context, summarized, and filtered. Replacements can be defined at search time or set and updated after the search. Only matches present in the vector are modified when files are written. Backup and restore helpers are provided for file workflows. Text that has already been read can also be searched and updated directly, giving users control over input, output, and encoding when needed. |
| Authors: | Sacha Martingay [aut, cre, cph] |
| Maintainer: | Sacha Martingay <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.2.0.9000 |
| Built: | 2026-09-29 11:46:25 UTC |
| Source: | https://github.com/smartiing/seekr |
as_seekr_path() converts paths to the normalized format used internally
by seekr for listing, filtering, matching, and replacement.
This is useful when writing custom exclude functions, comparing paths
with seekr results, or building file vectors before calling
filter_files() or match_files().
as_seekr_path(x)as_seekr_path(x)
x |
A character vector of paths. |
A seekr path is a character path that has been:
expanded, so ~ is resolved,
normalized, so redundant path components are removed,
resolved to an absolute path,
represented with forward slashes.
list_files() returns seekr paths: it starts from user-supplied
directories and returns the listed files as normalized absolute paths.
filter_files() and match_files() normalize their input path before
filtering or matching. This means path-based filters such as
path_pattern are applied to seekr paths, regardless of whether the input
paths were originally relative, absolute, or written with
platform-specific separators.
Normalizing paths makes path filtering more predictable. A path_pattern
is matched against a stable representation of the file path instead of
depending on how the path was originally written by the user.
A character vector of normalized absolute paths, with the same length and
order as x.
list_files(), filter_files(), match_files()
as_seekr_path(".") as_seekr_path(c(".", "~"))as_seekr_path(".") as_seekr_path(c(".", "~"))
seekr_match vectors to and from data framesas_tibble() and as.data.frame() convert a seekr_match vector into a
tibble or plain data frame, with one row per match and one column per field.
as_match() is the reverse: it converts a data frame back into a
seekr_match vector, validating all fields and checking for overlapping
matches within each file before returning.
Together, these functions unlock the full tidyverse toolkit for manipulating
match metadata. A typical pattern is to convert to a tibble, use
dplyr::mutate() or dplyr::filter() to derive or modify columns,
including the replacement field, and then convert back with as_match()
before calling replace_files().
## S3 method for class 'seekr_match' as_tibble(x, ...) ## S3 method for class 'seekr_match' as.data.frame(x, ...) as_match(x)## S3 method for class 'seekr_match' as_tibble(x, ...) ## S3 method for class 'seekr_match' as.data.frame(x, ...) as_match(x)
x |
For For |
... |
Not used. Present for compatibility with S3 generics. |
as_tibble() returns a tbl_df. as.data.frame() returns a plain
data.frame. In both cases the result has one row per match and one column
per field (see the Fields section of seekr_match).
as_match() returns a seekr_match vector. Matches within each file are
sorted by position. Overlapping or incoherent matches within the same file
cause an error.
Note: the empty_stage and exclusions attributes of the original
seekr_match vector are not preserved through the conversion.
seekr_match for the list of available fields.
filter_match() for simpler subsetting that does not require conversion.
vctrs::field() to access or modify a single field in place.
replace_files() to apply staged replacements after converting back.
## Not run: library(dplyr) ext_path <- system.file("extdata", package = "seekr") x <- seekr("TODO", path = ext_path) # Convert to tibble df <- as_tibble(x) df # Convert to plain data frame as.data.frame(x) # Convert back to seekr_match as_match(df) # Set a replacement for all matches df$replacement <- "DONE" as_match(df) # Suppose you want to replace `"foo"` with `"bar"`, but only for the last # match in each file, and only in files with at least three matches: x <- seekr("foo", "bar", path = ext_path) y <- x |> as_tibble() |> mutate( ith_match_per_file_rev = n():1L, n_match_per_file = n(), .by = path ) |> filter(ith_match_per_file_rev == 1L, n_match_per_file >= 3L) |> as_match() # replace_files(y) ## End(Not run)## Not run: library(dplyr) ext_path <- system.file("extdata", package = "seekr") x <- seekr("TODO", path = ext_path) # Convert to tibble df <- as_tibble(x) df # Convert to plain data frame as.data.frame(x) # Convert back to seekr_match as_match(df) # Set a replacement for all matches df$replacement <- "DONE" as_match(df) # Suppose you want to replace `"foo"` with `"bar"`, but only for the last # match in each file, and only in files with at least three matches: x <- seekr("foo", "bar", path = ext_path) y <- x |> as_tibble() |> mutate( ith_match_per_file_rev = n():1L, n_match_per_file = n(), .by = path ) |> filter(ith_match_per_file_rev == 1L, n_match_per_file >= 3L) |> as_match() # replace_files(y) ## End(Not run)
By default, seekr creates a backup before replace_files() and
restore_files() modify files. These functions let you inspect and manage
existing backups in a particular backup_dir.
list_backups() returns a tibble of all existing backups.
last_backup() returns only the most recent backup.
delete_backups() permanently deletes one or more backups by their id.
open_backup_dir() opens the current default backup directory.
list_backups(backup_dir = seekr_option("seekr.backup_dir")) last_backup(backup_dir = seekr_option("seekr.backup_dir")) delete_backups( id, ..., backup_dir = seekr_option("seekr.backup_dir"), .progress = seekr_option("seekr.progress") ) open_backup_dir(backup_dir = seekr_option("seekr.backup_dir"))list_backups(backup_dir = seekr_option("seekr.backup_dir")) last_backup(backup_dir = seekr_option("seekr.backup_dir")) delete_backups( id, ..., backup_dir = seekr_option("seekr.backup_dir"), .progress = seekr_option("seekr.progress") ) open_backup_dir(backup_dir = seekr_option("seekr.backup_dir"))
backup_dir |
Path to the backup directory. Defaults to
|
id |
Integer vector of backup ids to delete, as found in the |
... |
These dots are for future extensions and must be empty. |
.progress |
Whether to display progress messages. Default is |
list_backups() and last_backup() return a tibble as described in the
Backup table columns section. An empty tibble with the correct columns
is returned when no backups exist. last_backup() returns all rows belonging
to the most recent backup operation.
delete_backups() invisibly returns the paths of the deleted backup
subdirectories.
open_backup_dir() invisibly returns the path to the backup directory.
Backups are stored under backup_dir, which defaults to a platform-specific
user data directory managed by rappdirs::user_data_dir(). Each backup
occupies its own numbered subdirectory (000001/, 000002/, etc.)
containing a copy of each file and a backup.RDS metadata file.
The default backup directory can be changed globally with:
options(seekr.backup_dir = "/your/preferred/path")
list_backups() and last_backup() return a tibble with one row per
backed-up file and the following columns:
id: integer backup identifier, derived from the subdirectory name.
All rows belonging to the same backup operation share the same id.
created_at: date-time when the backup was created.
operation: either "replace" (backup created by replace_files())
or "restore" (backup created by restore_files()).
description: optional description provided at backup time, or NA.
original: absolute path to the original file.
backup: absolute path to the backup copy.
original_exists: whether the original file still exists on disk.
backup_exists: whether the backup copy still exists on disk.
size: size of the backup copy, as an fs_bytes value.
replace_files() to apply replacements and create backups.
restore_files() and restore_files_interactive() to restore files.
# Set up a temporary project with two files project_dir <- tempfile("seekr_project") dir.create(project_dir) backup_dir <- tempfile("seekr_backups") dir.create(backup_dir) file1 <- file.path(project_dir, "script1.R") file2 <- file.path(project_dir, "script2.R") writeLines("old_name <- function(x) x + 1", file1) writeLines("y <- old_name(2)", file2) # Search for the pattern we want to replace x <- seekr("old_name", replacement = "new_name", path = project_dir) x # No backup exists yet list_backups(backup_dir = backup_dir) # Apply the replacement; a backup is created automatically replace_files(x, backup_dir = backup_dir) # The backup now contains the original (pre-replacement) version # of both files, under id = 1 list_backups(backup_dir = backup_dir) # The files on disk have changed readLines(file1) readLines(file2) # Searching for the original pattern no longer finds anything seekr("old_name", path = project_dir) # Restore the files from the backup; this itself creates a second # backup (id = 2) of the current (replaced) version, before overwriting b <- last_backup(backup_dir = backup_dir) restore_files(from = b$backup, to = b$original, backup_dir = backup_dir) # We now have two backups: id = 1 is the state before replace_files(), # id = 2 is the state before restore_files() (i.e. the replaced version) list_backups(backup_dir = backup_dir) # The files are back to their original content readLines(file1) readLines(file2) # The original pattern is found again seekr("old_name", path = project_dir) ## Not run: # open_backup_dir() opens a file browser, so it is not run here open_backup_dir() # Review each file interactively before restoring restore_files_interactive(from = b$backup, to = b$original) # Customize the diff display (unified format) restore_files_interactive( from = b$backup, to = b$original, mode = "unified", color.mode = "yb" ) # Finally, particular backups in a backup_dir can be deleted b <- list_backups(backup_dir = backup_dir) b delete_backups(id = b$id, backup_dir = backup_dir) ## End(Not run) unlink(project_dir, recursive = TRUE) unlink(backup_dir, recursive = TRUE)# Set up a temporary project with two files project_dir <- tempfile("seekr_project") dir.create(project_dir) backup_dir <- tempfile("seekr_backups") dir.create(backup_dir) file1 <- file.path(project_dir, "script1.R") file2 <- file.path(project_dir, "script2.R") writeLines("old_name <- function(x) x + 1", file1) writeLines("y <- old_name(2)", file2) # Search for the pattern we want to replace x <- seekr("old_name", replacement = "new_name", path = project_dir) x # No backup exists yet list_backups(backup_dir = backup_dir) # Apply the replacement; a backup is created automatically replace_files(x, backup_dir = backup_dir) # The backup now contains the original (pre-replacement) version # of both files, under id = 1 list_backups(backup_dir = backup_dir) # The files on disk have changed readLines(file1) readLines(file2) # Searching for the original pattern no longer finds anything seekr("old_name", path = project_dir) # Restore the files from the backup; this itself creates a second # backup (id = 2) of the current (replaced) version, before overwriting b <- last_backup(backup_dir = backup_dir) restore_files(from = b$backup, to = b$original, backup_dir = backup_dir) # We now have two backups: id = 1 is the state before replace_files(), # id = 2 is the state before restore_files() (i.e. the replaced version) list_backups(backup_dir = backup_dir) # The files are back to their original content readLines(file1) readLines(file2) # The original pattern is found again seekr("old_name", path = project_dir) ## Not run: # open_backup_dir() opens a file browser, so it is not run here open_backup_dir() # Review each file interactively before restoring restore_files_interactive(from = b$backup, to = b$original) # Customize the diff display (unified format) restore_files_interactive( from = b$backup, to = b$original, mode = "unified", color.mode = "yb" ) # Finally, particular backups in a backup_dir can be deleted b <- list_backups(backup_dir = backup_dir) b delete_backups(id = b$id, backup_dir = backup_dir) ## End(Not run) unlink(project_dir, recursive = TRUE) unlink(backup_dir, recursive = TRUE)
empty_stage() retrieves the pipeline stage that produced an empty
seekr_match result returned by seek() or seekr().
Empty results can happen at different stages of the pipeline:
"input": the input path was empty.
"list": no files were found by list_files().
"filter": all files were excluded by filter_files().
"match": files were searched, but no match was found.
For non-empty results, empty_stage() returns NULL.
empty_stage(x)empty_stage(x)
x |
A |
Either NULL for non-empty results, or one of "input", "list",
"filter", or "match" for empty results.
ext_path <- system.file("extdata", package = "seekr") x <- seek("pattern_that_does_not_exist", path = ext_path) empty_stage(x)ext_path <- system.file("extdata", package = "seekr") x <- seek("pattern_that_does_not_exist", path = ext_path) empty_stage(x)
exclude_functions is the default named list of functions used by
filter_files() to exclude files that are usually not useful for text search.
exclude_functions is_git_dir(path) is_dependency_dir(path) is_minified_file(path) is_not_text_mime(path)exclude_functions is_git_dir(path) is_dependency_dir(path) is_minified_file(path) is_not_text_mime(path)
path |
A character vector of file paths to filter. |
A named list of exclude functions.
Each function receives a character vector of normalized file paths and must
return a logical vector of the same length. TRUE means that the
corresponding file should be excluded.
The default pipeline includes:
is_git_dir(): excludes files located inside .git/.
is_dependency_dir(): excludes files in common dependency folders such as
node_modules/, renv/, .venv/, vendor/, and __pycache__/.
is_minified_file(): excludes minified or bundled files such as .min.js,
.bundle.css, etc.
is_not_text_mime(): excludes files not recognized as text based on their
MIME type.
You can disable all exclude functions with exclude = NULL, remove
one of the defaults by setting it to NULL in a copy of
exclude_functions, or add your own named function to the list.
ext_path <- system.file("extdata", package = "seekr") files <- list_files(path = ext_path) names(exclude_functions) # Disable default exclude functions filter_files(files, exclude = NULL) # Add a custom exclude function my_fns <- exclude_functions my_fns$generated <- function(path) grepl("/generated/", path) filter_files(files, exclude = my_fns)ext_path <- system.file("extdata", package = "seekr") files <- list_files(path = ext_path) names(exclude_functions) # Disable default exclude functions filter_files(files, exclude = NULL) # Add a custom exclude function my_fns <- exclude_functions my_fns$generated <- function(path) grepl("/generated/", path) filter_files(files, exclude = my_fns)
exclusions() retrieves the exclusion details stored on objects returned by
filter_files(), seek(), and seekr().
exclusions(x)exclusions(x)
x |
An object containing exclusion details, typically the result of
|
A data frame with one row per input file and one column per exclusion filter,
describing which files were excluded and why. If no exclusion details are
available, returns NULL.
filter_files() for the filtering step, exclude_functions for the default
exclude-function pipeline.
ext_path <- system.file("extdata", package = "seekr") files <- list_files(path = ext_path) filtered <- filter_files(files, extension = "R") exclusions(filtered)ext_path <- system.file("extdata", package = "seekr") files <- list_files(path = ext_path) filtered <- filter_files(files, extension = "R") exclusions(filtered)
filter_files() keeps files matching extension and path_pattern, and
not exceeding max_file_size. Finally, the exclude functions are applied to
the remaining files, discarding common non-text or irrelevant files by default.
It is the second step of the seek() pipeline, applied after list_files()
and before match_files().
Exclusion filters are applied in this order:
extension: keeps only files whose extension is in the provided list.
path_pattern: keeps files whose normalized path matches the pattern.
max_file_size: excludes files larger than the given size.
exclude_functions: applies each named function to the remaining files.
Files are only passed to each subsequent filter if they have not already been excluded by a previous one.
Details about excluded files are stored on the result and can be retrieved
with exclusions().
filter_files( path, ..., extension = NULL, path_pattern = NULL, max_file_size = Inf, exclude = seekr::exclude_functions, .progress = seekr_option("seekr.progress") )filter_files( path, ..., extension = NULL, path_pattern = NULL, max_file_size = Inf, exclude = seekr::exclude_functions, .progress = seekr_option("seekr.progress") )
path |
A character vector of file paths to filter. |
... |
These dots are for future extensions and must be empty. |
extension |
Optional character vector of file extensions to keep. Either:
Extensions are normalized before matching: leading dots are stripped,
matching is case-insensitive, and duplicates are ignored. Only the last
component of compound extensions is used (e.g. |
path_pattern |
Optional pattern applied to filter normalized file paths (see
|
max_file_size |
Maximum file size in bytes. Files larger than this
value are excluded. Default is |
exclude |
Named list of functions used to exclude unwanted files during filtering. Either:
Defaults to exclude_functions, which excludes common non-text or irrelevant files. |
.progress |
Whether to display progress messages. Default is |
A character vector of normalized absolute paths that passed all filters.
Paths use the same representation as as_seekr_path().
An attribute "exclusions" is always attached to the result, containing
a data frame with one row per input file and one column per exclusion function,
detailing which files were excluded and why. Retrieve it with exclusions().
list_files() to produce the input paths.
match_files() to search the filtered files for a pattern.
exclusions() to inspect which files were removed and why.
seek() to run the full pipeline.
ext_path <- system.file("extdata", package = "seekr") files <- list_files(path = ext_path) # Keep only R files filter_files(files, extension = "R") # Keep only files in the R/ subfolder filter_files(files, path_pattern = "/R/") # Exclude files larger than 1 MB filter_files(files, max_file_size = fs::fs_bytes("1MB")) # Inspect which files were excluded and why filtered <- filter_files(files, extension = "R") exclusions(filtered) # Disable default exclude functions filter_files(files, exclude = NULL) # Add a custom exclude function my_fns <- exclude_functions my_fns$generated <- function(path) grepl("/generated/", path) filter_files(files, exclude = my_fns)ext_path <- system.file("extdata", package = "seekr") files <- list_files(path = ext_path) # Keep only R files filter_files(files, extension = "R") # Keep only files in the R/ subfolder filter_files(files, path_pattern = "/R/") # Exclude files larger than 1 MB filter_files(files, max_file_size = fs::fs_bytes("1MB")) # Inspect which files were excluded and why filtered <- filter_files(files, extension = "R") exclusions(filtered) # Disable default exclude functions filter_files(files, exclude = NULL) # Add a custom exclude function my_fns <- exclude_functions my_fns$generated <- function(path) grepl("/generated/", path) filter_files(files, exclude = my_fns)
filter_match() subsets a seekr_match vector using expressions
evaluated directly against its fields, without needing to call
vctrs::field() explicitly.
The two calls below are equivalent:
x[field(x, "start_line") > 10] filter_match(x, start_line > 10)
filter_match() is modelled after dplyr::filter(): field names can be
used directly in expressions, and multiple expressions are combined with
&. This makes it easy to write readable multi-condition filters:
x |> filter_match(
grepl("/R/", path),
match == "TODO",
start_line > 10
)
filter_match(x, ...)filter_match(x, ...)
x |
A |
... |
One or more filtering expressions, evaluated against the fields
of |
A seekr_match vector containing only the matches for which all
expressions evaluated to TRUE. If no expressions are supplied, x is
returned unchanged.
Unlike x[condition], filter_match() does not recycle logical vectors.
Each expression must return exactly length(x) values. Missing values
(NA) in any expression cause an error. This prevents silent mistakes from
implicit recycling or incomplete conditions.
vctrs::field() to access a field directly for use in base R subsetting.
as_tibble.seekr_match() and as_match() for more complex workflows that
require tabular manipulation.
replace_files() to apply staged replacements after filtering.
ext_path <- system.file("extdata", package = "seekr") x <- seekr("TODO|FIXME", path = ext_path) # Filter by line number filter_match(x, start_line > 10) # Filter by file path filter_match(x, grepl("/R/", path)) # Filter by matched text filter_match(x, match == "TODO") # Combine multiple conditions filter_match(x, match == "TODO", start_line > 10, grepl("/R/", path)) # Equivalent base R subsetting (more verbose) x[ field(x, "match") == "TODO" & field(x, "start_line") > 10 & grepl("/R/", field(x, "path")) ]ext_path <- system.file("extdata", package = "seekr") x <- seekr("TODO|FIXME", path = ext_path) # Filter by line number filter_match(x, start_line > 10) # Filter by file path filter_match(x, grepl("/R/", path)) # Filter by matched text filter_match(x, match == "TODO") # Combine multiple conditions filter_match(x, match == "TODO", start_line > 10, grepl("/R/", path)) # Equivalent base R subsetting (more verbose) x[ field(x, "match") == "TODO" & field(x, "start_line") > 10 & grepl("/R/", field(x, "path")) ]
list_files() starts from path and lists candidate files. It can recurse
into subdirectories with recurse, include hidden files and directories with
all, and optionally restrict discovery inside Git repositories with
use_git. It is the first step of the seek() pipeline.
Listing is intentionally simple: it does not know about patterns, extensions,
file sizes, or MIME types. Its only job is to turn directories into a
character vector of file paths. Filtering happens in the next step,
filter_files().
If use_git = TRUE, Git is used for each input path independently.
For each path, list_files() asks Git whether that path is inside a Git
repository. If it is, list_files() finds the repository root by walking
upward from that path, then keeps only the files also returned by
git ls-files --cached --others --exclude-standard for that repository.
Git is used to restrict the files discovered from the input path. It does not
expand the search. The path, recurse, and all arguments still define the
initial candidate files. For example, Git-tracked hidden files are not
returned unless all = TRUE, and Git-tracked files below the requested
recursion depth are not returned.
list_files() does not search downward for nested Git repositories. If an
input path is not inside a Git repository, it is listed normally, even if it
contains Git repositories in subdirectories. If you want Git-aware discovery
for nested repositories, pass those repository directories explicitly in
path.
If use_git = TRUE, Git must be installed and available on PATH.
The returned paths are normalized as described in as_seekr_path().
list_files( path = ".", ..., recurse = TRUE, all = FALSE, use_git = FALSE, .progress = seekr_option("seekr.progress") )list_files( path = ".", ..., recurse = TRUE, all = FALSE, use_git = FALSE, .progress = seekr_option("seekr.progress") )
path |
A character vector of one or more existing directories to
search in. Defaults to |
... |
These dots are for future extensions and must be empty. |
recurse |
Controls how deep the directory traversal goes to list files. Either:
|
all |
Whether to list hidden files and directories. Default is |
use_git |
Should Git be used to restrict file discovery inside Git
repositories? If |
.progress |
Whether to display progress messages. Default is |
A character vector of normalized absolute file paths. Returns an
empty character vector if no files are found or if path is empty.
filter_files() to filter the listed files before searching matches.
seek() to run the full listing, filtering, and matching pipeline.
ext_path <- system.file("extdata", package = "seekr") # List all files in the example directory list_files(path = ext_path) # List only files at the top level, without recursing list_files(path = ext_path, recurse = FALSE) # Recurse at most 2 levels deep list_files(path = ext_path, recurse = 2L) # Include hidden files and directories list_files(path = ext_path, all = TRUE) ## Not run: # Use Git to restrict discovery inside Git repositories list_files(path = ".", use_git = TRUE) ## End(Not run)ext_path <- system.file("extdata", package = "seekr") # List all files in the example directory list_files(path = ext_path) # List only files at the top level, without recursing list_files(path = ext_path, recurse = FALSE) # Recurse at most 2 levels deep list_files(path = ext_path, recurse = 2L) # Include hidden files and directories list_files(path = ext_path, all = TRUE) ## Not run: # Use Git to restrict discovery inside Git repositories list_files(path = ".", use_git = TRUE) ## End(Not run)
match_files() reads each file, decodes them using encoding, finds
pattern matches, and captures surrounding context lines. A replacement
can be provided to stage changes for later application with replace_files().
It is the third and final step of the seek() pipeline, applied after
list_files() and filter_files().
match_files( path, pattern, replacement = NULL, ..., context = 5L, encoding = "UTF-8", .progress = seekr_option("seekr.progress") )match_files( path, pattern, replacement = NULL, ..., context = 5L, encoding = "UTF-8", .progress = seekr_option("seekr.progress") )
path |
A character vector of file paths to read and search. |
pattern |
Pattern to search for, matched using stringr (ICU regular expressions). Either:
|
replacement |
Replacement to associate with each match. Replacements are computed immediately during the search and stored in the result. Either:
|
... |
These dots are for future extensions and must be empty. |
context |
Number of surrounding lines to capture around each match. Either:
|
encoding |
Encoding used to decode file content during the matching step. Either:
Note: |
.progress |
Whether to display progress messages. Default is |
A seekr_match vector. Each element represents one match and
carries the file path, match positions, matched text, optional replacement,
context lines, encoding, and a hash of the searched text used for replacement
safety. Returns an empty seekr_match vector when no matches are found.
Files that no longer exist before matching are skipped with a warning. Files that contain unsupported null bytes are also skipped with a warning. Other read or decoding errors abort.
For advanced use cases where you want to search for a pattern in-memory text,
see match_text().
match_text() to search for a pattern in in-memory text.
filter_files() to filter files before matching.
replace_files() to apply staged replacements.
ext_path <- system.file("extdata", package = "seekr") files <- ext_path |> list_files() |> filter_files(extension = "R") # Search for a pattern match_files(files, "TODO") # Capture more context lines match_files(files, "TODO", context = 10L) # Stage a replacement match_files(files, "old_fn", replacement = "new_fn")ext_path <- system.file("extdata", package = "seekr") files <- ext_path |> list_files() |> filter_files(extension = "R") # Search for a pattern match_files(files, "TODO") # Capture more context lines match_files(files, "TODO", context = 10L) # Stage a replacement match_files(files, "old_fn", replacement = "new_fn")
match_text() searches text that has already been read into R. It is the
text-level counterpart of match_files(): it does not read from disk and does
not record file encoding information.
match_text(text, path, pattern, replacement = NULL, ..., context = 5L)match_text(text, path, pattern, replacement = NULL, ..., context = 5L)
text |
Text content as a single string. |
path |
Source identifier associated with |
pattern |
Pattern to search for, matched using stringr (ICU regular expressions). Either:
|
replacement |
Replacement to associate with each match. Replacements are computed immediately during the search and stored in the result. Either:
|
... |
These dots are for future extensions and must be empty. |
context |
Number of surrounding lines to capture around each match. Either:
|
The resulting seekr_match vector can be inspected, summarized, filtered,
updated, and passed to replace_text(). It is not intended to be passed to
replace_files(), because seekr did not read the source file and does not
control how the text was decoded.
Use match_files() or seek()/seekr() for the usual file workflow.
A seekr_match vector.
text <- "Commodo labore culpa ullamco TODO irure laboris FIXME Lorem sunt sint" x <- match_text(text = text, path = "lorem.txt", pattern = "TODO") y <- match_text(text = text, path = "lorem.txt", pattern = "FIXME") z <- c(x, y) ztext <- "Commodo labore culpa ullamco TODO irure laboris FIXME Lorem sunt sint" x <- match_text(text = text, path = "lorem.txt", pattern = "TODO") y <- match_text(text = text, path = "lorem.txt", pattern = "FIXME") z <- c(x, y) z
print() displays a seekr_match vector in a readable console format,
grouped by source. Each printed match is shown with its source, match index,
line number, matched text, and, when available, its staged replacement.
The amount of output can be controlled with n, which limits the number of
matches printed. context controls how many surrounding lines are
shown.
## S3 method for class 'seekr_match' print(x, ..., n = NULL, context = 0L)## S3 method for class 'seekr_match' print(x, ..., n = NULL, context = 0L)
x |
A |
... |
Not used. Present for compatibility with the |
n |
Maximum number of matches to print. If |
context |
Number of context lines to print around each match. Either:
Only context lines captured when the |
Invisibly returns the original seekr_match vector x.
summary() for a compact summary of matches,
filter_match() to subset matches before printing, and seek() for the
context argument that controls how many surrounding lines are captured.
ext_path <- system.file("extdata", package = "seekr") x <- seekr( pattern = "(\\w+)(?= <- function)", replacement = toupper, path = ext_path ) # Print up to 10 matches (default) print(x) # Print all matches print(x, n = Inf) # Print only 3 matches print(x, n = 3L) # Reduce context to 1 line around each match print(x, context = 1L) # Show 3 lines before and 1 line after each match print(x, context = c(3L, 1L))ext_path <- system.file("extdata", package = "seekr") x <- seekr( pattern = "(\\w+)(?= <- function)", replacement = toupper, path = ext_path ) # Print up to 10 matches (default) print(x) # Print all matches print(x, n = Inf) # Print only 3 matches print(x, n = 3L) # Reduce context to 1 line around each match print(x, context = 1L) # Show 3 lines before and 1 line after each match print(x, context = c(3L, 1L))
replace_files() applies the replacements stored in a seekr_match object
to the corresponding files and writes (in UTF-8) the modified files back
to disk.
It is the final step of the usual seekr workflow: search with seek()/match_files(),
inspect or modify the resulting matches, then apply the replacements with replace_files().
replace_files( x, ..., backup = TRUE, description = NA_character_, allow_encoding_change = FALSE, backup_dir = seekr_option("seekr.backup_dir"), .progress = seekr_option("seekr.progress") )replace_files( x, ..., backup = TRUE, description = NA_character_, allow_encoding_change = FALSE, backup_dir = seekr_option("seekr.backup_dir"), .progress = seekr_option("seekr.progress") )
x |
A |
... |
These dots are for future extensions and must be empty. |
backup |
Whether to create a backup of the current files before overwriting
the files. Default is |
description |
Optional character string stored in the backup metadata
when |
allow_encoding_change |
Should |
backup_dir |
Path to the backup directory. Defaults to
|
.progress |
Whether to display progress messages. Default is |
replace_files() is designed to be conservative. Before any file is modified,
it checks that the replacements stored in x can still be applied safely.
Missing files are detected before the replacement process starts.
This safety check is intentionally performed in two passes. In the first pass,
before creating backups, replace_files() re-reads every target file and
verifies that its current text has the same hash as the text that was searched
when the matches were created. If any file fails this check, the operation
aborts before backups are created and before any file is modified. This helps
avoid partial replacements, for example replacing the first few files
successfully and then failing on a later file.
If all files pass the first check, backups are created when backup = TRUE.
Then replace_files() loops over the files again. For each file, it re-reads
the current content, verifies the hash a second time, applies the replacements
in memory, and writes the modified text back to disk in UTF-8. This second
check protects against concurrent edits that may happen between the initial
verification and the actual write.
Missing files are skipped with a warning. In that case, the returned
seekr_match object contains only the matches that were actually replaced.
Replacements are applied file by file. Within each file, matches are replaced from the end of the file to the beginning, so earlier replacements do not shift the recorded positions of later replacements.
By default, a backup of the current version of each modified file is created
before replacement. Use list_backups() and last_backup() to inspect
backups, and restore_files() or restore_files_interactive() to restore
them.
For advanced in-memory text workflows where you do not want seekr to read,
write, or create backups, use replace_text().
Invisibly returns the seekr_match object containing the matches that were
actually replaced. If missing files were skipped, matches from those files are
not included in the returned object.
seek() and seekr() to create matches with staged replacements.
filter_match() to keep only some matches before replacing.
list_backups() and last_backup() to inspect backups.
restore_files() and restore_files_interactive() to restore files.
# Set up a temporary project with two files project_dir <- tempfile("seekr_project") dir.create(project_dir) backup_dir <- tempfile("seekr_backups") dir.create(backup_dir) file1 <- file.path(project_dir, "script1.R") file2 <- file.path(project_dir, "script2.R") writeLines("old_name <- function(x) x + 1", file1) writeLines("y <- old_name(2)", file2) # Search for the pattern we want to replace x <- seekr("old_name", replacement = "new_name", path = project_dir) x # No backup exists yet list_backups(backup_dir = backup_dir) # Apply the replacement; a backup is created automatically replace_files(x, backup_dir = backup_dir) # The backup now contains the original (pre-replacement) version # of both files, under id = 1 list_backups(backup_dir = backup_dir) # The files on disk have changed readLines(file1) readLines(file2) # Searching for the original pattern no longer finds anything seekr("old_name", path = project_dir) # Restore the files from the backup; this itself creates a second # backup (id = 2) of the current (replaced) version, before overwriting b <- last_backup(backup_dir = backup_dir) restore_files(from = b$backup, to = b$original, backup_dir = backup_dir) # We now have two backups: id = 1 is the state before replace_files(), # id = 2 is the state before restore_files() (i.e. the replaced version) list_backups(backup_dir = backup_dir) # The files are back to their original content readLines(file1) readLines(file2) # The original pattern is found again seekr("old_name", path = project_dir) ## Not run: # open_backup_dir() opens a file browser, so it is not run here open_backup_dir() # Review each file interactively before restoring restore_files_interactive(from = b$backup, to = b$original) # Customize the diff display (unified format) restore_files_interactive( from = b$backup, to = b$original, mode = "unified", color.mode = "yb" ) # Finally, particular backups in a backup_dir can be deleted b <- list_backups(backup_dir = backup_dir) b delete_backups(id = b$id, backup_dir = backup_dir) ## End(Not run) unlink(project_dir, recursive = TRUE) unlink(backup_dir, recursive = TRUE)# Set up a temporary project with two files project_dir <- tempfile("seekr_project") dir.create(project_dir) backup_dir <- tempfile("seekr_backups") dir.create(backup_dir) file1 <- file.path(project_dir, "script1.R") file2 <- file.path(project_dir, "script2.R") writeLines("old_name <- function(x) x + 1", file1) writeLines("y <- old_name(2)", file2) # Search for the pattern we want to replace x <- seekr("old_name", replacement = "new_name", path = project_dir) x # No backup exists yet list_backups(backup_dir = backup_dir) # Apply the replacement; a backup is created automatically replace_files(x, backup_dir = backup_dir) # The backup now contains the original (pre-replacement) version # of both files, under id = 1 list_backups(backup_dir = backup_dir) # The files on disk have changed readLines(file1) readLines(file2) # Searching for the original pattern no longer finds anything seekr("old_name", path = project_dir) # Restore the files from the backup; this itself creates a second # backup (id = 2) of the current (replaced) version, before overwriting b <- last_backup(backup_dir = backup_dir) restore_files(from = b$backup, to = b$original, backup_dir = backup_dir) # We now have two backups: id = 1 is the state before replace_files(), # id = 2 is the state before restore_files() (i.e. the replaced version) list_backups(backup_dir = backup_dir) # The files are back to their original content readLines(file1) readLines(file2) # The original pattern is found again seekr("old_name", path = project_dir) ## Not run: # open_backup_dir() opens a file browser, so it is not run here open_backup_dir() # Review each file interactively before restoring restore_files_interactive(from = b$backup, to = b$original) # Customize the diff display (unified format) restore_files_interactive( from = b$backup, to = b$original, mode = "unified", color.mode = "yb" ) # Finally, particular backups in a backup_dir can be deleted b <- list_backups(backup_dir = backup_dir) b delete_backups(id = b$id, backup_dir = backup_dir) ## End(Not run) unlink(project_dir, recursive = TRUE) unlink(backup_dir, recursive = TRUE)
replace_text() is the in-memory counterpart of replace_files(). It applies
the replacements stored in a seekr_match object to text and returns the
modified text.
It does not read files, write files, or create backups. Use replace_files()
for the usual file-based workflow.
replace_text(text, x)replace_text(text, x)
text |
Text content as a single string. |
x |
A |
replace_text() verifies that the current text has the same hash as the text
that was searched when the matches were created. If the text has changed,
replacement is considered unsafe and the function aborts.
Matches are replaced from the end of the file to the beginning, so earlier replacements do not shift the recorded positions of later replacements.
replace_text() requires x to contain matches from a single source, the one
corresponding to text.
A single character string containing text after applying the replacements
stored in x.
match_text() to create matches from already-read text.
replace_files() to apply replacements directly to files.
filter_match() to keep only some matches before replacing.
text <- "hello old_name\nbye old_name" x <- match_text( text = text, path = "example.txt", pattern = "old_name", replacement = "new_name" ) replace_text(text, x)text <- "hello old_name\nbye old_name" x <- match_text( text = text, path = "example.txt", pattern = "old_name", replacement = "new_name" ) replace_text(text, x)
restore_files() copies files from a backup location back to their
original paths, overwriting the current versions. By default, it creates a
new backup of the current files before overwriting, so the restore itself
can be undone.
restore_files_interactive() works the same way but shows a side-by-side
diff (powered by diffobj::diffFile()) for each file before asking whether
to restore it. This is useful when you want to review the difference between
the current version of the file and its backup.
The typical workflow is to call list_backups() or last_backup() to find
the backup you want, then pass the backup and original columns to
restore_files():
b <- last_backup() restore_files(from = b$backup, to = b$original)
restore_files( from, to, ..., backup = TRUE, description = NA_character_, backup_dir = seekr_option("seekr.backup_dir"), .progress = seekr_option("seekr.progress") ) restore_files_interactive( from, to, ..., backup = TRUE, description = NA_character_, backup_dir = seekr_option("seekr.backup_dir"), .progress = seekr_option("seekr.progress") )restore_files( from, to, ..., backup = TRUE, description = NA_character_, backup_dir = seekr_option("seekr.backup_dir"), .progress = seekr_option("seekr.progress") ) restore_files_interactive( from, to, ..., backup = TRUE, description = NA_character_, backup_dir = seekr_option("seekr.backup_dir"), .progress = seekr_option("seekr.progress") )
from |
Character vector of source file paths to copy from. Typically
the |
to |
Character vector of destination file paths to copy to. Typically
the |
... |
For For |
backup |
Whether to create a backup of the current files before overwriting
the files. Default is |
description |
Optional character string stored in the backup metadata
when |
backup_dir |
Path to the backup directory. Defaults to
|
.progress |
Whether to display progress messages. Default is |
Both functions invisibly return the to paths of the files that were
actually restored. For restore_files() this is always the full to
vector. For restore_files_interactive() this is only the subset of files
the user chose to restore, an empty character vector if the user cancelled
or skipped all files.
restore_files_interactive() iterates over each file and shows a diff
between the backup (from) and the current version (to). For each file,
it prompts with the following choices:
Restore this file: restore this file and move to the next.
Ignore this file: skip this file and move to the next.
Restore all remaining files: restore this file and all subsequent ones without further prompts.
Ignore all remaining files: skip this file and all subsequent ones.
Cancel all planned changes: abort immediately without restoring anything, including files already confirmed in this session.
No files are modified until after the last prompt. All choices are
collected first, then applied in a single restore_files() call.
restore_files_interactive() requires the
diffobj package. It can only
be called in an interactive session.
list_backups() and last_backup() to find the backup you want to
restore.
delete_backups() to remove backups you no longer need.
replace_files() which creates a backup before writing any changes.
# Set up a temporary project with two files project_dir <- tempfile("seekr_project") dir.create(project_dir) backup_dir <- tempfile("seekr_backups") dir.create(backup_dir) file1 <- file.path(project_dir, "script1.R") file2 <- file.path(project_dir, "script2.R") writeLines("old_name <- function(x) x + 1", file1) writeLines("y <- old_name(2)", file2) # Search for the pattern we want to replace x <- seekr("old_name", replacement = "new_name", path = project_dir) x # No backup exists yet list_backups(backup_dir = backup_dir) # Apply the replacement; a backup is created automatically replace_files(x, backup_dir = backup_dir) # The backup now contains the original (pre-replacement) version # of both files, under id = 1 list_backups(backup_dir = backup_dir) # The files on disk have changed readLines(file1) readLines(file2) # Searching for the original pattern no longer finds anything seekr("old_name", path = project_dir) # Restore the files from the backup; this itself creates a second # backup (id = 2) of the current (replaced) version, before overwriting b <- last_backup(backup_dir = backup_dir) restore_files(from = b$backup, to = b$original, backup_dir = backup_dir) # We now have two backups: id = 1 is the state before replace_files(), # id = 2 is the state before restore_files() (i.e. the replaced version) list_backups(backup_dir = backup_dir) # The files are back to their original content readLines(file1) readLines(file2) # The original pattern is found again seekr("old_name", path = project_dir) ## Not run: # open_backup_dir() opens a file browser, so it is not run here open_backup_dir() # Review each file interactively before restoring restore_files_interactive(from = b$backup, to = b$original) # Customize the diff display (unified format) restore_files_interactive( from = b$backup, to = b$original, mode = "unified", color.mode = "yb" ) # Finally, particular backups in a backup_dir can be deleted b <- list_backups(backup_dir = backup_dir) b delete_backups(id = b$id, backup_dir = backup_dir) ## End(Not run) unlink(project_dir, recursive = TRUE) unlink(backup_dir, recursive = TRUE)# Set up a temporary project with two files project_dir <- tempfile("seekr_project") dir.create(project_dir) backup_dir <- tempfile("seekr_backups") dir.create(backup_dir) file1 <- file.path(project_dir, "script1.R") file2 <- file.path(project_dir, "script2.R") writeLines("old_name <- function(x) x + 1", file1) writeLines("y <- old_name(2)", file2) # Search for the pattern we want to replace x <- seekr("old_name", replacement = "new_name", path = project_dir) x # No backup exists yet list_backups(backup_dir = backup_dir) # Apply the replacement; a backup is created automatically replace_files(x, backup_dir = backup_dir) # The backup now contains the original (pre-replacement) version # of both files, under id = 1 list_backups(backup_dir = backup_dir) # The files on disk have changed readLines(file1) readLines(file2) # Searching for the original pattern no longer finds anything seekr("old_name", path = project_dir) # Restore the files from the backup; this itself creates a second # backup (id = 2) of the current (replaced) version, before overwriting b <- last_backup(backup_dir = backup_dir) restore_files(from = b$backup, to = b$original, backup_dir = backup_dir) # We now have two backups: id = 1 is the state before replace_files(), # id = 2 is the state before restore_files() (i.e. the replaced version) list_backups(backup_dir = backup_dir) # The files are back to their original content readLines(file1) readLines(file2) # The original pattern is found again seekr("old_name", path = project_dir) ## Not run: # open_backup_dir() opens a file browser, so it is not run here open_backup_dir() # Review each file interactively before restoring restore_files_interactive(from = b$backup, to = b$original) # Customize the diff display (unified format) restore_files_interactive( from = b$backup, to = b$original, mode = "unified", color.mode = "yb" ) # Finally, particular backups in a backup_dir can be deleted b <- list_backups(backup_dir = backup_dir) b delete_backups(id = b$id, backup_dir = backup_dir) ## End(Not run) unlink(project_dir, recursive = TRUE) unlink(backup_dir, recursive = TRUE)
seek() searches text files for a pattern and returns a seekr_match
vector. The result can be inspected, filtered, and passed to replace_files()
to apply replacements.
seekr() is a convenience wrapper around seek() that restricts the search
to R, R Markdown, and Quarto files (.R, .Rmd, .qmd).
list_files(), filter_files(), and match_files() are the three building
blocks of seek(). They can be called individually when you need more
control over each step.
list_files() starts from path, optionally use_git to restrict file
discovery, and recurses into subdirectories to list files. By default, not
all files are listed, with hidden files and directories excluded.
filter_files() keeps files matching extension and path_pattern and
not exceeding max_file_size. Finally, the exclude functions are applied to
the remaining files, discarding common non-text or irrelevant files by default.
match_files() reads each file, decodes them using encoding, finds
pattern matches, and captures surrounding context lines. A replacement
can be provided to stage changes for later application with replace_files().
seek( pattern, replacement = NULL, ..., path = ".", recurse = TRUE, all = FALSE, use_git = FALSE, extension = NULL, path_pattern = NULL, max_file_size = Inf, exclude = seekr::exclude_functions, context = 5L, encoding = "UTF-8", .progress = seekr_option("seekr.progress") ) seekr( pattern, replacement = NULL, ..., path = ".", recurse = TRUE, all = FALSE, use_git = FALSE, path_pattern = NULL, max_file_size = Inf, exclude = seekr::exclude_functions, context = 5L, encoding = "UTF-8", .progress = seekr_option("seekr.progress") )seek( pattern, replacement = NULL, ..., path = ".", recurse = TRUE, all = FALSE, use_git = FALSE, extension = NULL, path_pattern = NULL, max_file_size = Inf, exclude = seekr::exclude_functions, context = 5L, encoding = "UTF-8", .progress = seekr_option("seekr.progress") ) seekr( pattern, replacement = NULL, ..., path = ".", recurse = TRUE, all = FALSE, use_git = FALSE, path_pattern = NULL, max_file_size = Inf, exclude = seekr::exclude_functions, context = 5L, encoding = "UTF-8", .progress = seekr_option("seekr.progress") )
pattern |
Pattern to search for, matched using stringr (ICU regular expressions). Either:
|
replacement |
Replacement to associate with each match. Replacements are computed immediately during the search and stored in the result. Either:
|
... |
These dots are for future extensions and must be empty. |
path |
A character vector of one or more existing directories to
search in. Defaults to |
recurse |
Controls how deep the directory traversal goes to list files. Either:
|
all |
Whether to list hidden files and directories. Default is |
use_git |
Should Git be used to restrict file discovery inside Git
repositories? If |
extension |
Optional character vector of file extensions to keep. Either:
Extensions are normalized before matching: leading dots are stripped,
matching is case-insensitive, and duplicates are ignored. Only the last
component of compound extensions is used (e.g. |
path_pattern |
Optional pattern applied to filter normalized file paths (see
|
max_file_size |
Maximum file size in bytes. Files larger than this
value are excluded. Default is |
exclude |
Named list of functions used to exclude unwanted files during filtering. Either:
Defaults to exclude_functions, which excludes common non-text or irrelevant files. |
context |
Number of surrounding lines to capture around each match. Either:
|
encoding |
Encoding used to decode file content during the matching step. Either:
Note: |
.progress |
Whether to display progress messages. Default is |
A seekr_match vector. Each element represents one match and carries the
file path, match position, matched text, optional replacement, context
lines, encoding, and a hash of the searched text used for replacement safety.
The vector is always returned, even when empty.
An attribute "exclusions" is attached to the result after filtering, containing
a data frame with one row per input file and one column per exclusion function,
detailing which files were excluded and why. Retrieve it with exclusions().
If the result is empty, use empty_stage() to see whether the pipeline became
empty during input, listing, filtering, or matching.
seekr_match for the match object structure and available methods.
print.seekr_match() and summary.seekr_match() to inspect results.
filter_match() to subset matches.
replace_files() to apply replacements.
# Create a small temporary project to search in example_dir <- tempfile("seekr-example") dir.create(example_dir) dir.create(file.path(example_dir, "R")) dir.create(file.path(example_dir, "tests")) dir.create(file.path(example_dir, "data")) writeLines( c( "old_fn <- function(x) {", " # TODO: rename foo", " foo + x", "}" ), file.path(example_dir, "R", "code.R") ) writeLines( c( "test_that('foo works', {", " # TODO: update test", " expect_equal(foo, 1)", "})" ), file.path(example_dir, "tests", "test-code.R") ) writeLines( c( "name,value", "foo,1", "bar,2" ), file.path(example_dir, "data", "values.csv") ) # seek() is built from three lower-level functions files <- list_files(example_dir) filtered <- filter_files(files, extension = "R", path_pattern = "/R/") x <- match_files(filtered, "foo", "bar") # These functions can be piped y <- example_dir |> list_files() |> filter_files(extension = "R", path_pattern = "/R/") |> match_files("foo", "bar") identical(x, y) # This is equivalent to the seek() call below z <- seek("foo", "bar", path = example_dir, extension = "R", path_pattern = "/R/") identical(y, z) # Search for a pattern in all text files x <- seek("TODO", path = example_dir) print(x) # Search only in R files seek("TODO", path = example_dir, extension = "R") # Search only in a specific subfolder seek("TODO", path = example_dir, path_pattern = "/R/") # seekr() is a shortcut for searching R, R Markdown, and Quarto files seekr("old_fn", path = example_dir) # Stage a plain string replacement x <- seek("old_fn", "new_fn", path = example_dir) x # Stage replacements with a function x <- seek( "foo|bar", replacement = function(x) ifelse(x == "foo", "bar", "foo"), path = example_dir ) x # Stage replacements after searching x <- seekr("foo|bar", path = example_dir) field(x, "replacement") <- ifelse(field(x, "match") == "foo", "bar", "foo") x # Create a temporary backup directory backup_dir <- tempfile("seekr-backup") dir.create(backup_dir) # Apply replacements after inspection replace_files(x, backup_dir = backup_dir) # Restore files from the latest backup bck <- last_backup(backup_dir = backup_dir) restore_files(from = bck$backup, to = bck$original, backup_dir = backup_dir) # See which files were excluded exclusions(x) # empty_stage() explains where the pipeline became empty dir.create(file.path(example_dir, "empty")) empty_stage(seek("foo", path = character())) empty_stage(seek("foo", path = file.path(example_dir, "empty"))) empty_stage(seek("foo", path = example_dir, extension = "dummy")) empty_stage(seek("missing_pattern", path = example_dir)) # Remove the two temporary directories unlink(backup_dir, recursive = TRUE) unlink(example_dir, recursive = TRUE)# Create a small temporary project to search in example_dir <- tempfile("seekr-example") dir.create(example_dir) dir.create(file.path(example_dir, "R")) dir.create(file.path(example_dir, "tests")) dir.create(file.path(example_dir, "data")) writeLines( c( "old_fn <- function(x) {", " # TODO: rename foo", " foo + x", "}" ), file.path(example_dir, "R", "code.R") ) writeLines( c( "test_that('foo works', {", " # TODO: update test", " expect_equal(foo, 1)", "})" ), file.path(example_dir, "tests", "test-code.R") ) writeLines( c( "name,value", "foo,1", "bar,2" ), file.path(example_dir, "data", "values.csv") ) # seek() is built from three lower-level functions files <- list_files(example_dir) filtered <- filter_files(files, extension = "R", path_pattern = "/R/") x <- match_files(filtered, "foo", "bar") # These functions can be piped y <- example_dir |> list_files() |> filter_files(extension = "R", path_pattern = "/R/") |> match_files("foo", "bar") identical(x, y) # This is equivalent to the seek() call below z <- seek("foo", "bar", path = example_dir, extension = "R", path_pattern = "/R/") identical(y, z) # Search for a pattern in all text files x <- seek("TODO", path = example_dir) print(x) # Search only in R files seek("TODO", path = example_dir, extension = "R") # Search only in a specific subfolder seek("TODO", path = example_dir, path_pattern = "/R/") # seekr() is a shortcut for searching R, R Markdown, and Quarto files seekr("old_fn", path = example_dir) # Stage a plain string replacement x <- seek("old_fn", "new_fn", path = example_dir) x # Stage replacements with a function x <- seek( "foo|bar", replacement = function(x) ifelse(x == "foo", "bar", "foo"), path = example_dir ) x # Stage replacements after searching x <- seekr("foo|bar", path = example_dir) field(x, "replacement") <- ifelse(field(x, "match") == "foo", "bar", "foo") x # Create a temporary backup directory backup_dir <- tempfile("seekr-backup") dir.create(backup_dir) # Apply replacements after inspection replace_files(x, backup_dir = backup_dir) # Restore files from the latest backup bck <- last_backup(backup_dir = backup_dir) restore_files(from = bck$backup, to = bck$original, backup_dir = backup_dir) # See which files were excluded exclusions(x) # empty_stage() explains where the pipeline became empty dir.create(file.path(example_dir, "empty")) empty_stage(seek("foo", path = character())) empty_stage(seek("foo", path = file.path(example_dir, "empty"))) empty_stage(seek("foo", path = example_dir, extension = "dummy")) empty_stage(seek("missing_pattern", path = example_dir)) # Remove the two temporary directories unlink(backup_dir, recursive = TRUE) unlink(example_dir, recursive = TRUE)
seekr_match vectorsA seekr_match is an S3 vector built on vctrs::new_rcrd() that represents
the matches found by seek(), seekr(), match_files(), or match_text().
Each element corresponds to a single match and stores its source path, position, matched text, optional replacement, surrounding context lines, encoding, and a hash of the searched text used for replacement safety.
new_seekr_match( path = character(), start_line = integer(), end_line = integer(), start = integer(), end = integer(), start_col = integer(), end_col = integer(), match = character(), replacement = character(), before = character(), line = character(), after = character(), encoding = character(), hash = character() )new_seekr_match( path = character(), start_line = integer(), end_line = integer(), start = integer(), end = integer(), start_col = integer(), end_col = integer(), match = character(), replacement = character(), before = character(), line = character(), after = character(), encoding = character(), hash = character() )
path |
A character vector of source identifiers. For file workflows,
these are normalized absolute file paths. For |
start_line, end_line
|
Integer vectors. 1-based line numbers where each match begins and ends. |
start, end
|
Integer vectors. 1-based absolute character positions where each match begins and ends. |
start_col, end_col
|
Integer vectors. 1-based column positions of the match start and end within their respective lines. |
match |
A character vector. The exact text matched. |
replacement |
A character vector. The staged replacement for each
match. |
before |
A character vector. Context lines preceding each match. |
line |
A character vector. The complete line(s) containing each match. |
after |
A character vector. Context lines following each match. |
encoding |
A character vector. The encoding used to read each file. |
hash |
A character vector. Hash of the searched text, used to check that it has not changed before replacement. |
new_seekr_match() returns an empty or populated seekr_match vector.
new_seekr_match() is a low-level constructor intended primarily for internal
use and advanced extensions. In normal usage, create seekr_match vectors with
seek(), seekr(), match_files(), or match_text().
Access any field with vctrs::field() which is re-exported by seekr:
field(x, "path") field(x, "match")
seekr_match objects support the following S3 methods:
print(): displays matches in a formatted view with optional context and replacement preview.
summary(): summarizes matches by file, matched text, replacement, extension, and encoding.
str(): shows the internal field structure with types and sample values.
tibble::as_tibble(): converts to a tibble for advanced manipulation.
The following functions are also commonly used with seekr_match vectors:
as_match(): converts a tibble back to a seekr_match vector.
filter_match(): subsets matches using dplyr-style expressions.
sort_within_files(): reorders matches within each file while
preserving file order.
In addition to its fields, a seekr_match vector returned by seek() or seekr()
may carry two attributes:
empty_stage: when the vector is empty, indicates which pipeline step
produced no output. Retrieve with empty_stage().
exclusions: when files were removed during filtering, a data frame
detailing which files were excluded and by which function. Retrieve with
exclusions().
These attributes are dropped when combining seekr_match vectors.
seek() and seekr() to search for matches and produce seekr_match
vectors.
print.seekr_match() and summary.seekr_match() to inspect results.
filter_match() to subset matches before replacing.
replace_files() to apply staged replacements.
empty_stage() and exclusions() to diagnose empty results.
# Produce a seekr_match vector ext_path <- system.file("extdata", package = "seekr") x <- seekr("function", toupper, path = ext_path) # Access a field field(x, "path") field(x, "match") # Subset head(x, 3) # Combine two seekr_match vectors y <- seekr("Hello", path = ext_path) c(x, y)# Produce a seekr_match vector ext_path <- system.file("extdata", package = "seekr") x <- seekr("function", toupper, path = ext_path) # Access a field field(x, "path") field(x, "match") # Subset head(x, 3) # Combine two seekr_match vectors y <- seekr("Hello", path = ext_path) c(x, y)
seekr_option() returns the resolved value of a single seekr option.
It first checks whether the user has set the option with base::options().
If not, it returns the package default. The returned value is validated before
being returned.
This function is mostly useful for seekr's own default arguments, such as:
.progress = seekr_option("seekr.progress")
In most cases, users should configure seekr with base::options() and inspect
available options with seekr_options().
seekr_option(name)seekr_option(name)
name |
Name of the seekr option to retrieve, as a single string. See
|
The resolved option value. The returned type depends on the option:
logical for seekr.progress.
character for all other options (paths, print symbols, and ANSI style
codes).
seekr_options() to list all available options and their defaults.
seekr_option("seekr.progress") seekr_option("seekr.print.mode") options(seekr.print.mode = "plain") seekr_option("seekr.print.mode") options(seekr.print.mode = NULL) seekr_option("seekr.print.mode")seekr_option("seekr.progress") seekr_option("seekr.print.mode") options(seekr.print.mode = "plain") seekr_option("seekr.print.mode") options(seekr.print.mode = NULL) seekr_option("seekr.print.mode")
seekr_options() returns the options that control seekr's global behavior,
along with their current user-defined value and their package default.
These options can be changed with base::options(). For example:
options(seekr.progress = FALSE) options(seekr.print.mode = "plain")
The current column reports the value currently set with base::options().
If an option has not been set by the user, current is NA and seekr falls
back to the value shown in default.
The main options are:
seekr.progress: whether seekr displays progress messages by default.
seekr.backup_dir: directory where backups are stored.
seekr.print.mode: print mode, either "rich", "color", or "plain".
seekr.print.tab: symbol used to display tab characters.
seekr.print.newline: symbol used to display newline characters when printing deleted newlines.
seekr.style.*: ANSI style codes used internally by rich printing.
The seekr.style.* options are intentionally low-level. They accept ANSI SGR
codes as strings, such as "31", "1;31", or "38;5;243".
seekr_options()seekr_options()
A tibble with one row per seekr option and the following columns:
name: option name.
current: value currently set by the user, or NA if unset.
default: default value used by seekr when the option is unset.
seekr_option() to retrieve the resolved value of a single option.
seekr_options() # Disable progress messages globally options(seekr.progress = FALSE) seekr_options() # Reset the option options(seekr.progress = NULL)seekr_options() # Disable progress messages globally options(seekr.progress = FALSE) seekr_options() # Reset the option options(seekr.progress = NULL)
sort_within_files() sorts matches by position within each file, while
preserving the order in which files appear in x.
This differs from sort(), which sorts globally and reorders files
alphabetically. Use sort_within_files() when you want positions within
each file to be consistent — for example, after combining two
seekr_match vectors with vctrs::vec_c() — without changing the order
in which files are displayed or processed.
sort_within_files(x)sort_within_files(x)
x |
A |
A seekr_match vector with the same matches in potentially
different order: files appear in their original order, and matches within
each file are sorted by start, then end, then match, then
replacement.
ext_path <- system.file("extdata", package = "seekr") x <- seekr("TODO", path = ext_path) y <- seekr("FIXME", path = ext_path) # Combine and reorder within files without changing file order z <- vctrs::vec_c(x, y) sort_within_files(z)ext_path <- system.file("extdata", package = "seekr") x <- seekr("TODO", path = ext_path) y <- seekr("FIXME", path = ext_path) # Combine and reorder within files without changing file order z <- vctrs::vec_c(x, y) sort_within_files(z)
seekr_match vectorstr() displays the internal structure of a seekr_match vector: the
name, type, and sample values of each field, formatted for the console
width.
This is useful for a quick overview of what a seekr_match vector
contains without printing the full formatted output produced by
print.seekr_match().
## S3 method for class 'seekr_match' str(object, ...)## S3 method for class 'seekr_match' str(object, ...)
object |
A |
... |
Not used. Present for compatibility with the |
Invisibly returns the seekr_match vector.
ext_path <- system.file("extdata", package = "seekr") x <- seekr("TODO", path = ext_path) str(x)ext_path <- system.file("extdata", package = "seekr") x <- seekr("TODO", path = ext_path) str(x)
summary() produces a compact overview of a seekr_match vector:
the most frequent files, matched texts, file extensions, and encodings.
When replacements are staged, matched texts are displayed together with their
replacement preview, giving a high-level picture of what replace_files()
would change.
## S3 method for class 'seekr_match' summary(object, ...) ## S3 method for class 'summary_seekr_match' print(x, ..., n = NULL)## S3 method for class 'seekr_match' summary(object, ...) ## S3 method for class 'summary_seekr_match' print(x, ..., n = NULL)
object |
A |
... |
Not used. Present for compatibility with the |
x |
A |
n |
Maximum number of rows to print in each summary table. If |
An object of class summary_seekr_match, containing summary tables for
files, matches/replacements, extensions, and encodings. Print it with
print() to display a formatted summary in the console.
print.seekr_match() for a full match-level display with context lines.
str.seekr_match() for the internal field structure.
ext_path <- system.file("extdata", package = "seekr") x <- seekr("TODO", path = ext_path) y <- seekr("TODO", "DONE", path = ext_path) summary(x) summary(y) summary(c(x, y))ext_path <- system.file("extdata", package = "seekr") x <- seekr("TODO", path = ext_path) y <- seekr("TODO", "DONE", path = ext_path) summary(x) summary(y) summary(c(x, y))
This helper wraps a function to indicate that it should receive the full capture group matrix as input, instead of the default vector of full matches. This allows the replacement logic to use individual capture groups.
with_capture_groups_matrix(fn)with_capture_groups_matrix(fn)
fn |
A function taking a single argument: a character matrix of capture groups, where each row corresponds to a match, the first column is the full match and subsequent columns are capture groups. |
The capture matrix is the result of stringr::str_match_all(): the first
column contains the full match, and subsequent columns contain capture groups.
A function identical to fn, but marked with an internal attribute
used by compute_replacement() to dispatch on replacement logic.
text <- "lorem ipsum foo_bar lorem ipsum bar_foo lorem ipsum" fn_repl <- function(M) paste0(tolower(M[, 3L]), ".", toupper(M[, 2L])) fn_repl <- with_capture_groups_matrix(fn_repl) match_text(text, path = "example", pattern = "(\\w+)_(\\w+)", replacement = fn_repl)text <- "lorem ipsum foo_bar lorem ipsum bar_foo lorem ipsum" fn_repl <- function(M) paste0(tolower(M[, 3L]), ".", toupper(M[, 2L])) fn_repl <- with_capture_groups_matrix(fn_repl) match_text(text, path = "example", pattern = "(\\w+)_(\\w+)", replacement = fn_repl)