Skip to main content

generate_code_actions

Function generate_code_actions 

Source
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:

  1. At most one QUICKFIX “fix vulnerability” action, if versions carries an OSV scan result flagging this dependency, crate::osv::DependencyVulnerabilities::recommended_fix has a claimable target F, and F’s own fix_target_status (populated by deps-lsp’s phase B, #462) clears it as a verified fix — see the private build_vulnerability_fix_action helper just above. This action is computed entirely from versions and the dependency’s declared requirement, deliberately before the registry.get_versions call 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.
  2. At most one QUICKFIX “fix unsatisfiable requirement” action, computed the same registry-independent way (see the private build_unsatisfiable_fix_action helper 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.
  3. Up to five plain REFACTOR “update to <version>” actions, one per non-yanked version crate::completion::prepare_version_display_items selects from the registry response. Each action’s edit text comes from crate::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.1 stays ==1.0.2 rather 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_items lists 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’s truncate_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_date answers “does latest already 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.