pub async fn generate_code_actions<R: Registry + ?Sized>(
parse_result: &dyn ParseResult,
position: Position,
uri: &Uri,
versions: VersionData<'_>,
content: &str,
registry: &R,
formatter: &dyn EcosystemFormatter,
) -> Vec<CodeAction>Expand description
Generates the code actions offered for the dependency at position.
Finds the dependency whose declared version position falls on
(formatter.is_position_on_dependency). Returns an empty Vec
immediately if no dependency is at position, it has no version_range
to edit, it has no declared (or an empty) version_requirement, or the
literal-span guard rejects it — content sliced over version_range
no longer holds the literal text (see literal_span_matches, compared
against Dependency::version_literal when the ecosystem provides one,
falling back to version_requirement otherwise — e.g. a Maven
${property} reference or a Gradle DSL variable/alias). Writing a
TextEdit at that range would corrupt the manifest instead of fixing it,
so this mirrors the guard collect_update_all_edits already applies on
the bulk-edit path, and gates every kind of action below since any of
them could write there.
Otherwise returns up to three kinds of action, in this order:
- At most one
QUICKFIX“fix vulnerability” action, ifversionscarries an OSV scan result flagging this dependency,crate::osv::DependencyVulnerabilities::recommended_fixhas a claimable target F, and F’s ownfix_target_status(populated bydeps-lsp’s phase B, #462) clears it as a verified fix — see the privatebuild_vulnerability_fix_actionhelper just above. This action is computed entirely fromversionsand the dependency’s declared requirement, deliberately before theregistry.get_versionscall below — a registry outage must never hide a known-vulnerable dependency’s fix (FR-007), so this action is still returned even when the registry fetch that produces the plain list below fails. This does not extend to an OSV outage or unavailability: F’s verification is an OSV-derived precondition (fix_target_status), not a registry one, so an OSV outage — or a code-action request racing ahead of phase B completing — degrades to omitting this action entirely rather than presenting an unverified F as verified (FR-004, fail-safe). When the registry fetch does succeed, a fix target the registry reports as yanked is dropped rather than offered. - At most one
QUICKFIX“fix unsatisfiable requirement” action, computed the same registry-independent way (see the privatebuild_unsatisfiable_fix_actionhelper just above) for the same FR-007 reason. If its rewritten text collides with the vulnerability fix’s (both yank-filtered first), it is dropped in favor of the vulnerability fix, whose title is the more informative of the two. - Up to five plain
REFACTOR“update to<version>” actions, one per non-yanked versioncrate::completion::prepare_version_display_itemsselects from the registry response. Each action’s edit text comes fromcrate::lsp_helpers::PackageRendering::format_version_replacing, which preserves the manifest’s existing pin/operator style where an ecosystem overrides it (e.g. PyPI’s==1.0.1stays==1.0.2rather than expanding to a>=,<range). Every entry’s formatted edit text is checked against a running set seeded with the declared requirement and the two fix actions’ own formatted text (whitespace-insensitive); an entry is skipped, and never added to the set, when its text is already present. This is the common case, not a rare edge case:crate::completion::prepare_version_display_itemslists the top 5 non-yanked registry versions newest-first, so whenever the declared version is already within 5 releases of latest, it is itself one of the display items being offered as an “update”. The same set also catches two display items whose formatted text coincides — e.g. an ecosystem formatter that truncates precision (PyPI’struncate_release_to_match) can map several distinct registry versions to the same rewritten text — and a display item matching a fix action’s target even when their raw versions differ (formatting can normalize two distinct inputs to the same text). Textual (not semantic) equality is deliberate:formatter.is_requirement_up_to_dateanswers “doeslatestalready satisfy this requirement”, which is true for e.g.is_requirement_up_to_date("^1.0", "1.2.0")and would wrongly suppress every explicit-bump action for a range-style requirement; it also can’t detect a pinned no-op like==1.0.0->==1.0.0, since it never compares the formatted edit text at all.
Every action above is built with is_preferred: None; exactly one is promoted to
Some(true) in a single post-pass once all three kinds have been considered, in
priority order: the vulnerability fix, then the unsatisfiable fix, then the REFACTOR
item whose item.is_latest is set. LSP’s isPreferred is a flat per-response boolean
with no per-diagnostic scoping, so “at most one preferred action” must hold across all
producers, not per producer — building every action with None and resolving the flag
once here, rather than at each construction site, is what keeps that invariant
structural (checkable with one filter().count() <= 1 assertion) instead of something
every future producer has to remember to uphold by hand. A vulnerability is silent and
security-relevant; an unsatisfiable requirement is loud (the package manager already
fails the build) but merely inconvenient; both outrank a routine “update to latest”.
This resolution runs on every return path below that can carry a fix action, including
the registry-outage path, so an outage never silently drops isPreferred from an
already-built fix.
Returns an empty Vec also when no fix action applies and the registry fetch fails.
No # Examples here: exercising this meaningfully needs a Registry
impl plus ParseResult/Dependency mocks, which live as private test
fixtures in the sibling test_support module rather than as public
API — see the generate_code_actions_* tests here for realistic calls.