Skip to content

Commit

Permalink
solidify "pal directory" idea (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
simonpcouch committed Oct 11, 2024
1 parent 835b819 commit 62df561
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 206 deletions.
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ S3method(print,pal_response)
export(.pal_add)
export(.pal_add_dir)
export(.pal_addin)
export(.pal_dir)
export(.pal_init)
export(.pal_new)
import(rlang)
importFrom(elmer,content_image_file)
importFrom(glue,glue)
96 changes: 1 addition & 95 deletions R/pal-add-remove.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#' the function a role and prompt, the pal will be available on the command
#' palette.
#'
#' @param role A single string giving the [.pal_init()] role.
#' @param role A single string giving a descriptor of the pal's functionality.
# TODO: actually do this once elmer implements
#' @param prompt A file path to a markdown file giving the system prompt or
#' the output of [elmer::interpolate()].
Expand All @@ -18,36 +18,6 @@
#' pals" section below.
#'
#' @section Adding multiple, persistent pals:

#' Pals can also be added in batch with `.pal_add_dir()`, which takes a directory
#' of markdown files. Prompts are markdown files with the
#' name `role-interface.md`, where interface is one of
#' `r glue::glue_collapse(glue::double_quote(supported_interfaces), ", ", last = " or ")`.
#' An example directory might look like:
#'
#' ```
#' /
#' ├── .config/
#' │ └── pal/
#' │ ├── proofread-replace.md
#' │ └── summarize-prefix.md
#' ```
#'
#' In that case, pal will register two custom pals when you call `library(pal)`.
#' One of them has the role "proofread" and will replace the selected text with
#' a proofread version (according to the instructions contained in the markdown
#' file itself). The other has the role "summarize" and will prefix the selected
#' text with a summarized version (again, according to the markdown file's
#' instructions). Note:
#'
#' * Files without a `.md` extension are ignored.
#' * Files with a `.md` extension must contain only one hyphen in their filename,
#' and the text following the hyphen must be one of `replace`, `prefix`, or
#' `suffix`.
#'
#' To load custom prompts every time the package is loaded, place your
#' prompts in `~/.config/pal` (or, to use some other folder, set
#' `options(.pal_dir = some_dir)` before loading the package).
#'
#' @returns
#' `NULL`, invisibly. Called for its side effect: a pal with role `role`
Expand Down Expand Up @@ -124,67 +94,3 @@ parse_interface <- function(interface, role, call = caller_env()) {

paste0(".pal_rs_", role)
}

# mapping over multiple calls to `.pal_add()` ----------------------------------
#' @rdname pal_add_remove
#' @export
.pal_add_dir <- function(dir) {
prompt_paths <- list.files(dir, full.names = TRUE)
roles_and_interfaces <- roles_and_interfaces(prompt_paths)

for (idx in seq_along(prompt_paths)) {
role <- roles_and_interfaces[[idx]][1]
prompt <- paste0(readLines(prompt_paths[idx]), collapse = "\n")
interface <- roles_and_interfaces[[idx]][2]

.pal_add(role = role, prompt = prompt, interface = interface)
}
}

roles_and_interfaces <- function(prompt_paths) {
prompt_basenames <- basename(prompt_paths)
prompt_basenames <- grep("\\.md$", prompt_basenames, value = TRUE)
prompt_basenames <- filter_single_hyphenated(prompt_basenames)

roles_and_interfaces <- gsub("\\.md$", "", prompt_basenames)
roles_and_interfaces <- strsplit(roles_and_interfaces, "-")
roles_and_interfaces <- filter_interfaces(roles_and_interfaces)

roles_and_interfaces
}

filter_single_hyphenated <- function(x) {
has_one_hyphen <- grepl("^[^-]*-[^-]*$", x)
if (any(!has_one_hyphen)) {
cli::cli_inform(
"Prompt{?s} {.val {paste0(x[!has_one_hyphen], '.md')}} must contain
a single hyphen in {?its/their} filename{?s} and will not
be registered with pal.",
call = NULL
)
}

x[has_one_hyphen]
}

filter_interfaces <- function(x) {
interfaces <- lapply(x, `[[`, 2)
recognized <- interfaces %in% supported_interfaces
if (any(!recognized)) {
prompts <- vapply(x, paste0, character(1), collapse = "-")
cli::cli_inform(
c(
"Prompt{?s} {.val {paste0(prompts[!recognized], '.md')}} {?has/have} an
unrecognized {.arg interface} noted in {?its/their} filename{?s}
and will not be registered with pal.",
"{.arg interface} (following the hyphen) must be one of
{.or {.code {supported_interfaces}}}."
),
call = NULL
)
}

x[recognized]
}


115 changes: 115 additions & 0 deletions R/pal-dir.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#' The pal directory
#'
#' @description
#' Pals can be added in batch with `.pal_add_dir()`, which takes a directory
#' of markdown files. On package load, pal will add all pals defined in the pal
#' directory, located at `.pal_dir()` (by default, `~/.config/pal`). To add a
#' new pal that will be loaded every time the package is loaded, use `.pal_new()`.
#'
#' @param dir Path to a directory of markdown files--see `Details` for more.
#' @inheritParams pal_add_remove
#'
#' @details
#' Prompts are markdown files with the
#' name `role-interface.md`, where interface is one of
#' `r glue::glue_collapse(glue::double_quote(supported_interfaces), ", ", last = " or ")`.
#' An example directory might look like:
#'
#' ```
#' /
#' ├── .config/
#' │ └── pal/
#' │ ├── proofread-replace.md
#' │ └── summarize-prefix.md
#' ```
#'
#' In that case, pal will register two custom pals when you call `library(pal)`.
#' One of them has the role "proofread" and will replace the selected text with
#' a proofread version (according to the instructions contained in the markdown
#' file itself). The other has the role "summarize" and will prefix the selected
#' text with a summarized version (again, according to the markdown file's
#' instructions). Note:
#'
#' * Files without a `.md` extension are ignored.
#' * Files with a `.md` extension must contain only one hyphen in their filename,
#' and the text following the hyphen must be one of `replace`, `prefix`, or
#' `suffix`.
#'
#' To load custom prompts every time the package is loaded, place your
#' prompts in `~/.config/pal` (or, to use some other folder, set
#' `options(.pal_dir = some_dir)` before loading the package).
#' @name pal_add_dir
#' @export
.pal_add_dir <- function(dir) {
prompt_paths <- list.files(dir, full.names = TRUE)
roles_and_interfaces <- roles_and_interfaces(prompt_paths)

for (idx in seq_along(prompt_paths)) {
role <- roles_and_interfaces[[idx]][1]
prompt <- paste0(readLines(prompt_paths[idx]), collapse = "\n")
interface <- roles_and_interfaces[[idx]][2]

.pal_add(role = role, prompt = prompt, interface = interface)
}
}

roles_and_interfaces <- function(prompt_paths) {
prompt_basenames <- basename(prompt_paths)
prompt_basenames <- grep("\\.md$", prompt_basenames, value = TRUE)
prompt_basenames <- filter_single_hyphenated(prompt_basenames)

roles_and_interfaces <- gsub("\\.md$", "", prompt_basenames)
roles_and_interfaces <- strsplit(roles_and_interfaces, "-")
roles_and_interfaces <- filter_interfaces(roles_and_interfaces)

roles_and_interfaces
}

filter_single_hyphenated <- function(x) {
has_one_hyphen <- grepl("^[^-]*-[^-]*$", x)
if (any(!has_one_hyphen)) {
cli::cli_inform(
"Prompt{?s} {.val {paste0(x[!has_one_hyphen], '.md')}} must contain
a single hyphen in {?its/their} filename{?s} and will not
be registered with pal.",
call = NULL
)
}

x[has_one_hyphen]
}

filter_interfaces <- function(x) {
interfaces <- lapply(x, `[[`, 2)
recognized <- interfaces %in% supported_interfaces
if (any(!recognized)) {
prompts <- vapply(x, paste0, character(1), collapse = "-")
cli::cli_inform(
c(
"Prompt{?s} {.val {paste0(prompts[!recognized], '.md')}} {?has/have} an
unrecognized {.arg interface} noted in {?its/their} filename{?s}
and will not be registered with pal.",
"{.arg interface} (following the hyphen) must be one of
{.or {.code {supported_interfaces}}}."
),
call = NULL
)
}

x[recognized]
}

#' @rdname pal_add_dir
#' @export
.pal_dir <- function() {
getOption(".pal_dir", default = file.path("~", ".config", "pal"))
}

#' @rdname pal_add_dir
#' @export
.pal_new <- function(role, interface) {
check_string(role)
arg_match0(interface, supported_interfaces)

file.edit(paste0(.pal_dir(), "/", role, "-", interface, ".md"))
}
1 change: 1 addition & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ reference:
- title: Infrastructure
contents:
- pal_add_remove
- pal_add_dir
- .pal_init
- .pal_addin
61 changes: 61 additions & 0 deletions man/pal_add_dir.Rd

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

34 changes: 1 addition & 33 deletions man/pal_add_remove.Rd

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

30 changes: 0 additions & 30 deletions tests/testthat/_snaps/pal-add-remove.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,33 +54,3 @@
Error in `.pal_remove()`:
! No active pal with the given `role`.

# filter_single_hyphenated messages informatively

Code
res <- filter_single_hyphenated(x)
Message
Prompts "basename.md" and "base_name.md" must contain a single hyphen in their filenames and will not be registered with pal.

---

Code
res <- filter_single_hyphenated(x[1:2])
Message
Prompt "basename.md" must contain a single hyphen in its filename and will not be registered with pal.

# filter_interfaces messages informatively

Code
res <- filter_interfaces(x)
Message
Prompts "bop-bad.md" and "boop-silly.md" have an unrecognized `interface` noted in their filenames and will not be registered with pal.
`interface` (following the hyphen) must be one of `replace`, `prefix`, or `suffix`.

---

Code
res <- filter_interfaces(x[1:2])
Message
Prompt "bop-bad.md" has an unrecognized `interface` noted in its filename and will not be registered with pal.
`interface` (following the hyphen) must be one of `replace`, `prefix`, or `suffix`.

Loading

0 comments on commit 62df561

Please sign in to comment.