Skip to content

Commit d0569cc

Browse files
authored
Add pkg_detail RPC for packages pane detail editor (#1291)
Adds a `.ps.rpc.pkg_detail(name)` RPC that returns cheap, kernel-local detail metadata for a single installed package. Positron's new package detail editor calls this when a package is opened. The RPC reads the installed package's `DESCRIPTION` via `utils::packageDescription(...)` and returns a named list with: - `name` - `dependencyCount` - count of `Depends` + `Imports` + `LinkingTo`, excluding base packages and `R` itself - `title` - the `Title` field (when present) - `author` - `Maintainer`, falling back to `Author` (when present) - `license` - the `License` field (when present) - `sourceRepository` - the `Repository` field, e.g. "CRAN" (when present) - `publishedDate` - the `Date/Publication` field (when present) It returns `NULL` for an empty name or a package that isn't installed. No Rust change is needed - the generic `.ps.rpc.<method>` dispatch in `crates/ark/src/ui/ui_comm.rs` already routes it. Companion Positron work: **posit-dev/positron#12926** (the package detail editor, which adds the `getPackageDetail` package-manager hook that calls this RPC). The Positron PR will bump the ark submodule to this change once merged. Positron PR: posit-dev/positron#14440 ## QA In an R session: ```r .ps.rpc.pkg_detail("utils") # base pkg: name/title/author/license, dependencyCount 0 .ps.rpc.pkg_detail("dplyr") # CRAN pkg: + sourceRepository, publishedDate, dependencyCount > 0 .ps.rpc.pkg_detail("nope") # NULL ```
1 parent 881e84a commit d0569cc

1 file changed

Lines changed: 69 additions & 0 deletions

File tree

crates/ark/src/modules/positron/packages_pane.R

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,75 @@
115115
as.list(version)
116116
}
117117

118+
# Return detail fields for a single installed package by name.
119+
#' @export
120+
.ps.rpc.pkg_detail <- function(name) {
121+
if (!nzchar(name)) {
122+
return(NULL)
123+
}
124+
fields <- c(
125+
"Title",
126+
"Author",
127+
"Maintainer",
128+
"License",
129+
"Repository",
130+
"Date/Publication"
131+
)
132+
# Read straight from the package's DESCRIPTION. packageDescription() returns
133+
# a length-1 NA (and warns) when the package isn't installed, which is far
134+
# cheaper than installed.packages() -- that parses every installed package's
135+
# DESCRIPTION just to answer an existence check.
136+
d <- suppressWarnings(utils::packageDescription(name, fields = fields))
137+
if (!inherits(d, "packageDescription")) {
138+
return(NULL)
139+
}
140+
141+
# Collapse DCF whitespace; return NULL for missing (NA) fields.
142+
clean <- function(x) {
143+
if (is.null(x) || is.na(x)) {
144+
return(NULL)
145+
}
146+
trimws(gsub("\\s+", " ", x, perl = TRUE))
147+
}
148+
149+
# Reduce an R License field to its primary license: the first alternative
150+
# (before "|"), without the "+ file LICENSE" clause.
151+
primary_license <- function(x) {
152+
if (is.null(x)) {
153+
return(NULL)
154+
}
155+
first <- trimws(strsplit(x, "\\|")[[1]][1])
156+
first <- trimws(sub(
157+
"\\s*\\+\\s*file\\s+.*$",
158+
"",
159+
first,
160+
ignore.case = TRUE
161+
))
162+
if (!nzchar(first)) {
163+
return(NULL)
164+
}
165+
first
166+
}
167+
168+
# Prefer Maintainer for author display; fall back to Author.
169+
author <- clean(d$Maintainer)
170+
if (is.null(author)) {
171+
author <- clean(d$Author)
172+
}
173+
174+
# Drop absent (NULL) fields so the other side only sees populated keys.
175+
out <- list(
176+
name = name,
177+
title = clean(d$Title),
178+
author = author,
179+
license = primary_license(clean(d$License)),
180+
sourceRepository = clean(d$Repository),
181+
publishedDate = clean(d[["Date/Publication"]])
182+
)
183+
Filter(Negate(is.null), out)
184+
}
185+
186+
118187
# Return the list of outdated packages with their latest available versions.
119188
#
120189
# `utils::old.packages()` queries the user's configured repositories, so

0 commit comments

Comments
 (0)