pub fn collect_update_all_edits(
parse_result: &dyn ParseResult,
content: &str,
versions: VersionData<'_>,
formatter: &dyn EcosystemFormatter,
) -> Vec<TextEdit>Expand description
Manifest edits bringing every safely-editable outdated dependency to latest.
A dependency is included when all of the following hold:
- it declares a
version_range(a span to rewrite exists); - a
latestversion is known inversions.cached(normalized name first, then raw — mirroringcrate::lsp_helpers::generate_diagnostics_from_cache); formatter.is_requirement_up_to_datereports the declared requirement as not satisfyinglatest— the same predicate diagnostics use, so on a fixture where the guard below is a no-op,collect_update_all_edits(..).len()equals the number ofgenerate_diagnostics_from_cache“Newer version available” diagnostics;- the literal-span guard (
literal_span_matches):contentsliced overversion_rangemust still be (up to whitespace and NuGet’s bracket wrap) the literal text —Dependency::version_literalwhen the ecosystem provides one (e.g.deps-swift, whose synthesized comparator requirement string diverges from the bare literalversion_rangespans), falling back to the declared requirement text otherwise. Some ecosystems pointversion_rangeat something that is not a version literal at all — a Maven${property}reference or a Gradle DSL variable/version-catalog alias — and rewriting those spans would corrupt the manifest instead of fixing it. A dependency that fails the guard is skipped entirely: neither counted nor edited.
Accepted edits are sorted by start position; a later edit whose start falls before the
previous edit’s end (an overlap — a WorkspaceEdit protocol violation) is dropped with
a tracing::warn!. No current parser produces overlapping version_ranges, so this is
a guard against future parser changes, not an expected code path.
content is the manifest source, needed for the literal-span guard above — the same
parameter Ecosystem::generate_completions
already threads through for a similar reason.
§Examples
use deps_core::lsp_helpers::{
collect_update_all_edits, DiagnosticMessages, DiagnosticPolicy, OsvNaming, PackageNaming,
PackageRendering, PackageVersions, RequirementResolution, SourcePolicy, VersionData,
};
use deps_core::{ConcreteVersion, Dependency, ParseResult, PackageName, VersionReq};
use std::any::Any;
use std::collections::HashMap;
use tower_lsp_server::ls_types::{Position, Range, Uri};
struct MockFormatter;
impl PackageNaming for MockFormatter {}
impl PackageRendering for MockFormatter {
fn format_version_for_text_edit(&self, version: &ConcreteVersion) -> String {
version.to_string()
}
fn package_url(&self, name: &PackageName) -> String {
format!("https://example.com/{name}")
}
}
impl RequirementResolution for MockFormatter {}
impl DiagnosticMessages for MockFormatter {}
impl DiagnosticPolicy for MockFormatter {}
impl SourcePolicy for MockFormatter {}
impl OsvNaming for MockFormatter {}
struct MockDep {
name: PackageName,
version_req: VersionReq,
version_range: Range,
name_range: Range,
}
impl Dependency for MockDep {
fn name(&self) -> &PackageName { &self.name }
fn name_range(&self) -> Range { self.name_range }
fn version_requirement(&self) -> Option<&VersionReq> { Some(&self.version_req) }
fn version_range(&self) -> Option<Range> { Some(self.version_range) }
fn source(&self) -> deps_core::parser::DependencySource {
deps_core::parser::DependencySource::Registry
}
fn as_any(&self) -> &dyn Any { self }
}
struct MockParseResult { deps: Vec<MockDep>, uri: Uri }
impl ParseResult for MockParseResult {
fn dependencies(&self) -> Vec<&dyn Dependency> {
self.deps.iter().map(|d| d as &dyn Dependency).collect()
}
fn workspace_root(&self) -> Option<&std::path::Path> { None }
fn uri(&self) -> &Uri { &self.uri }
fn as_any(&self) -> &dyn Any { self }
}
let content = r#"serde = "1.0.0""#;
let parse_result = MockParseResult {
deps: vec![MockDep {
name: PackageName::new("serde"),
version_req: VersionReq::new("1.0.0"),
version_range: Range::new(Position::new(0, 9), Position::new(0, 14)),
name_range: Range::new(Position::new(0, 0), Position::new(0, 5)),
}],
uri: deps_core::test_util::test_uri("/test/Cargo.toml"),
};
let mut cached = HashMap::new();
cached.insert("serde".into(), PackageVersions::latest_only("1.2.0"));
let resolved = HashMap::new();
let edits = collect_update_all_edits(
&parse_result,
content,
VersionData::new(&cached, &resolved),
&MockFormatter,
);
assert_eq!(edits.len(), 1);
assert_eq!(edits[0].new_text, "1.2.0");