pub trait Ecosystem:
Send
+ Sync
+ Sealed {
Show 21 methods
// Required methods
fn id(&self) -> &'static str;
fn display_name(&self) -> &'static str;
fn manifest_filenames(&self) -> &[&'static str];
fn parse_manifest<'a>(
&'a self,
content: &'a str,
uri: &'a Uri,
) -> BoxFuture<'a, Result<Box<dyn ParseResult>>>;
fn registry(&self) -> Arc<dyn Registry>;
fn formatter(&self) -> &dyn EcosystemFormatter;
fn generate_completions<'a>(
&'a self,
parse_result: &'a dyn ParseResult,
position: Position,
content: &'a str,
freshness: FreshnessSettings,
) -> BoxFuture<'a, Completions>;
fn as_any(&self) -> &dyn Any;
// Provided methods
fn manifest_extensions(&self) -> &[&'static str] { ... }
fn manifest_patterns(&self) -> &[&'static str] { ... }
fn manifest_directory_patterns(&self) -> &[(&'static str, &'static str)] { ... }
fn lockfile_filenames(&self) -> &[&'static str] { ... }
fn watched_config_filenames(&self) -> &[&'static str] { ... }
fn lockfile_provider(&self) -> Option<Arc<dyn LockFileProvider>> { ... }
fn generate_inlay_hints<'a>(
&'a self,
parse_result: &'a dyn ParseResult,
versions: VersionData<'a>,
loading_state: LoadingState,
config: &'a EcosystemConfig,
) -> BoxFuture<'a, Vec<InlayHint>> { ... }
fn generate_hover<'a>(
&'a self,
parse_result: &'a dyn ParseResult,
position: Position,
versions: VersionData<'a>,
freshness: FreshnessSettings,
) -> BoxFuture<'a, Option<Hover>> { ... }
fn generate_code_actions<'a>(
&'a self,
parse_result: &'a dyn ParseResult,
position: Position,
uri: &'a Uri,
versions: VersionData<'a>,
content: &'a str,
) -> BoxFuture<'a, Vec<CodeAction>> { ... }
fn generate_diagnostics<'a>(
&'a self,
parse_result: &'a dyn ParseResult,
versions: VersionData<'a>,
uri: &'a Uri,
freshness: FreshnessSettings,
severities: DiagnosticSeverities,
) -> BoxFuture<'a, Vec<Diagnostic>> { ... }
fn generate_document_links(
&self,
_parse_result: &dyn ParseResult,
_uri: &Uri,
) -> Vec<DocumentLink> { ... }
fn generate_code_lenses<'a>(
&'a self,
parse_result: &'a dyn ParseResult,
content: &'a str,
versions: VersionData<'a>,
uri: &'a Uri,
command_id: &'a str,
) -> BoxFuture<'a, Vec<CodeLens>> { ... }
fn package_search_is_incomplete(&self) -> bool { ... }
}Expand description
Main trait that all ecosystem implementations must implement.
Each ecosystem (Cargo, npm, PyPI, etc.) provides its own implementation. This trait defines the contract for parsing manifests, fetching registry data, and generating LSP responses.
§Type Erasure
This trait uses Box<dyn Trait> instead of associated types to allow
runtime polymorphism and dynamic ecosystem registration.
§Examples
use deps_core::{Ecosystem, ParseResult, Registry, EcosystemConfig, PackageName, ConcreteVersion};
use deps_core::completion::Completions;
use deps_core::lsp_helpers::{
DiagnosticMessages, DiagnosticPolicy, EcosystemFormatter, OsvNaming, PackageNaming,
PackageRendering, RequirementResolution, SourcePolicy,
};
use std::sync::Arc;
use std::any::Any;
use tower_lsp_server::ls_types::{Uri, CompletionItem, Position};
struct MyFormatter;
impl PackageNaming for MyFormatter {}
impl PackageRendering for MyFormatter {
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 MyFormatter {}
impl DiagnosticMessages for MyFormatter {}
impl DiagnosticPolicy for MyFormatter {}
impl SourcePolicy for MyFormatter {}
impl OsvNaming for MyFormatter {}
struct MyEcosystem {
registry: Arc<dyn Registry>,
formatter: MyFormatter,
}
impl deps_core::ecosystem::private::Sealed for MyEcosystem {}
impl Ecosystem for MyEcosystem {
fn id(&self) -> &'static str { "my-ecosystem" }
fn display_name(&self) -> &'static str { "My Ecosystem" }
fn manifest_filenames(&self) -> &[&'static str] { &["my-manifest.toml"] }
fn parse_manifest<'a>(
&'a self,
_content: &'a str,
_uri: &'a Uri,
) -> deps_core::ecosystem::BoxFuture<'a, deps_core::error::Result<Box<dyn ParseResult>>> {
Box::pin(async move { todo!() })
}
fn registry(&self) -> Arc<dyn Registry> { self.registry.clone() }
fn formatter(&self) -> &dyn EcosystemFormatter { &self.formatter }
fn generate_completions<'a>(
&'a self,
_parse_result: &'a dyn ParseResult,
_position: Position,
_content: &'a str,
_freshness: deps_core::FreshnessSettings,
) -> deps_core::ecosystem::BoxFuture<'a, Completions> {
Box::pin(async move { Completions::default() })
}
fn as_any(&self) -> &dyn Any { self }
}Required Methods§
Sourcefn id(&self) -> &'static str
fn id(&self) -> &'static str
Unique identifier (e.g., “cargo”, “npm”, “pypi”)
This identifier is used for ecosystem registration and routing.
Sourcefn display_name(&self) -> &'static str
fn display_name(&self) -> &'static str
Human-readable name (e.g., “Cargo (Rust)”, “npm (JavaScript)”)
This name is displayed in diagnostic messages and logs.
Sourcefn manifest_filenames(&self) -> &[&'static str]
fn manifest_filenames(&self) -> &[&'static str]
Manifest filenames this ecosystem handles (e.g., [“Cargo.toml”])
The ecosystem registry uses these filenames to route file URIs to the appropriate ecosystem implementation.
Sourcefn parse_manifest<'a>(
&'a self,
content: &'a str,
uri: &'a Uri,
) -> BoxFuture<'a, Result<Box<dyn ParseResult>>>
fn parse_manifest<'a>( &'a self, content: &'a str, uri: &'a Uri, ) -> BoxFuture<'a, Result<Box<dyn ParseResult>>>
Sourcefn registry(&self) -> Arc<dyn Registry>
fn registry(&self) -> Arc<dyn Registry>
Get the registry client for this ecosystem
The registry provides version lookup and package search capabilities.
Sourcefn formatter(&self) -> &dyn EcosystemFormatter
fn formatter(&self) -> &dyn EcosystemFormatter
Get the ecosystem-specific formatter for LSP response generation.
The formatter handles version comparison, package URLs, and text formatting. Override this to customize LSP response generation.
Sourcefn generate_completions<'a>(
&'a self,
parse_result: &'a dyn ParseResult,
position: Position,
content: &'a str,
freshness: FreshnessSettings,
) -> BoxFuture<'a, Completions>
fn generate_completions<'a>( &'a self, parse_result: &'a dyn ParseResult, position: Position, content: &'a str, freshness: FreshnessSettings, ) -> BoxFuture<'a, Completions>
Generate completions for a position.
Provides autocomplete suggestions for package names and versions.
freshness.enabled gates whether version completion items carry a
relative-age label_details suffix (issue #145); implementations that
delegate to crate::completion::complete_versions_generic get this for
free by threading freshness through.
The returned Completions::is_incomplete must reflect this specific call
(the completion context actually served), not a static worst case for the
ecosystem as a whole (#427): a package-name search over an unranked,
truncated index should report true, while a version completion or any
other exhaustive context in the same manifest must report false, even for
an ecosystem where some contexts are incomplete and others are not.
Provided Methods§
Sourcefn manifest_extensions(&self) -> &[&'static str]
fn manifest_extensions(&self) -> &[&'static str]
File extensions this ecosystem handles when the manifest basename is
not fixed (e.g. [".csproj", ".fsproj"] for NuGet project files).
Consulted by crate::EcosystemRegistry::get_for_filename only after
an exact manifest_filenames match
fails. Empty by default, indicating this ecosystem is routed solely by
exact filename.
Sourcefn manifest_patterns(&self) -> &[&'static str]
fn manifest_patterns(&self) -> &[&'static str]
Basename glob patterns this ecosystem handles, each containing exactly
one * wildcard (e.g. ["requirements*.txt"]).
Consulted by crate::EcosystemRegistry::get_for_filename as a third
routing stage, tried after an exact
manifest_filenames match fails and
before manifest_extensions — for
basenames that are neither fixed nor identified by extension alone
(e.g. requirements.txt, requirements-dev.txt). Empty by default.
Matching is case-sensitive, unlike the extension stage: these patterns
target canonically-lowercase filenames (pip, Renovate and Dependabot
all treat requirements.txt as lowercase), whereas the extension
stage exists specifically for Windows/MSBuild project files whose
case genuinely varies.
Sourcefn manifest_directory_patterns(&self) -> &[(&'static str, &'static str)]
fn manifest_directory_patterns(&self) -> &[(&'static str, &'static str)]
(directory_path, file_suffix) pairs identifying a file solely by its
containing directory path and suffix. directory_path may be a single
segment (e.g. [("requirements", ".txt")] for Python’s
requirements/base.txt split-file layout) or multiple /-joined
segments (e.g. [(".github/workflows", ".yml")] for GitHub Actions
workflow files) — either way it is matched against the tail of the
file’s directory path on segment boundaries, not just the immediate
parent, so a multi-segment pattern matches regardless of how many
ancestor directories precede it. Used when the basename alone carries
no ecosystem signal.
Consulted by crate::EcosystemRegistry::get_for_uri only, after both
manifest_patterns and
manifest_extensions miss on the
basename — it needs the full path, so it is never reachable from
crate::EcosystemRegistry::get_for_filename. Empty by default.
Sourcefn lockfile_filenames(&self) -> &[&'static str]
fn lockfile_filenames(&self) -> &[&'static str]
Lock file filenames this ecosystem uses (e.g., [“Cargo.lock”])
Used for file watching - LSP will monitor changes to these files and refresh UI when they change. Returns empty slice if ecosystem doesn’t use lock files.
§Default Implementation
Returns empty slice by default, indicating no lock files are used.
Sourcefn watched_config_filenames(&self) -> &[&'static str]
fn watched_config_filenames(&self) -> &[&'static str]
Non-lockfile config filenames this ecosystem resolves during Self::parse_manifest
(e.g. ["pnpm-workspace.yaml", ".npmrc"] for npm’s catalog and registry resolution),
whose values end up baked into a manifest’s ParseResult rather than looked up
separately the way a Self::lockfile_provider is.
Used for file watching alongside Self::lockfile_filenames — LSP monitors changes
to these files too, but reacts by fully re-parsing every open document of this
ecosystem (not merely refreshing cached resolved versions, since the value isn’t kept
separately from the parse result to refresh in place). Returns empty slice by default.
Sourcefn lockfile_provider(&self) -> Option<Arc<dyn LockFileProvider>>
fn lockfile_provider(&self) -> Option<Arc<dyn LockFileProvider>>
Get the lock file provider for this ecosystem.
Returns None if the ecosystem doesn’t support lock files.
Lock files provide resolved dependency versions without network requests.
Sourcefn generate_inlay_hints<'a>(
&'a self,
parse_result: &'a dyn ParseResult,
versions: VersionData<'a>,
loading_state: LoadingState,
config: &'a EcosystemConfig,
) -> BoxFuture<'a, Vec<InlayHint>>
fn generate_inlay_hints<'a>( &'a self, parse_result: &'a dyn ParseResult, versions: VersionData<'a>, loading_state: LoadingState, config: &'a EcosystemConfig, ) -> BoxFuture<'a, Vec<InlayHint>>
Generate inlay hints for the document.
Default implementation delegates to lsp_helpers::generate_inlay_hints
using self.formatter(). Override only if custom behavior is needed.
Sourcefn generate_hover<'a>(
&'a self,
parse_result: &'a dyn ParseResult,
position: Position,
versions: VersionData<'a>,
freshness: FreshnessSettings,
) -> BoxFuture<'a, Option<Hover>>
fn generate_hover<'a>( &'a self, parse_result: &'a dyn ParseResult, position: Position, versions: VersionData<'a>, freshness: FreshnessSettings, ) -> BoxFuture<'a, Option<Hover>>
Generate hover information for a position.
Default implementation delegates to lsp_helpers::generate_hover
using self.formatter() and self.registry().
Sourcefn generate_code_actions<'a>(
&'a self,
parse_result: &'a dyn ParseResult,
position: Position,
uri: &'a Uri,
versions: VersionData<'a>,
content: &'a str,
) -> BoxFuture<'a, Vec<CodeAction>>
fn generate_code_actions<'a>( &'a self, parse_result: &'a dyn ParseResult, position: Position, uri: &'a Uri, versions: VersionData<'a>, content: &'a str, ) -> BoxFuture<'a, Vec<CodeAction>>
Generate code actions for a position.
Default implementation delegates to lsp_helpers::generate_code_actions
using self.formatter() and self.registry(). versions carries the
same OSV scan results generate_hover and generate_diagnostics use,
so a vulnerable dependency at position gets a “fix vulnerability”
quickfix alongside the plain version-update actions. content is the
manifest source, needed to guard against rewriting a version_range
that no longer slices to its declared requirement text (see
lsp_helpers::literal_span_matches).
Sourcefn generate_diagnostics<'a>(
&'a self,
parse_result: &'a dyn ParseResult,
versions: VersionData<'a>,
uri: &'a Uri,
freshness: FreshnessSettings,
severities: DiagnosticSeverities,
) -> BoxFuture<'a, Vec<Diagnostic>>
fn generate_diagnostics<'a>( &'a self, parse_result: &'a dyn ParseResult, versions: VersionData<'a>, uri: &'a Uri, freshness: FreshnessSettings, severities: DiagnosticSeverities, ) -> BoxFuture<'a, Vec<Diagnostic>>
Generate diagnostics for the document.
Default implementation delegates to lsp_helpers::generate_diagnostics_from_cache
using self.formatter().
Sourcefn generate_document_links(
&self,
_parse_result: &dyn ParseResult,
_uri: &Uri,
) -> Vec<DocumentLink>
fn generate_document_links( &self, _parse_result: &dyn ParseResult, _uri: &Uri, ) -> Vec<DocumentLink>
Generate textDocument/documentLink targets for the document.
A document link is a clickable reference from a byte range in this
manifest to another resource — e.g. a -r other.txt / -c constraints.txt reference inside a pip requirements file, resolved
to the absolute file it points at. Purely local (no registry access),
so unlike the other generate_* methods this is synchronous rather
than a BoxFuture. Empty by default: most ecosystems’ manifest
formats have no such intra-file-graph references.
Sourcefn generate_code_lenses<'a>(
&'a self,
parse_result: &'a dyn ParseResult,
content: &'a str,
versions: VersionData<'a>,
uri: &'a Uri,
command_id: &'a str,
) -> BoxFuture<'a, Vec<CodeLens>>
fn generate_code_lenses<'a>( &'a self, parse_result: &'a dyn ParseResult, content: &'a str, versions: VersionData<'a>, uri: &'a Uri, command_id: &'a str, ) -> BoxFuture<'a, Vec<CodeLens>>
Generate the “Update N outdated dependencies” code lens for the document.
Default implementation delegates to lsp_helpers::generate_code_lenses using
self.formatter(). Override only if custom behavior is needed.
Sourcefn package_search_is_incomplete(&self) -> bool
fn package_search_is_incomplete(&self) -> bool
Whether this ecosystem’s package-name search may return a truncated view of
a larger candidate set (see e.g. PypiRegistry::search’s doc comment).
generate_completions already reports
this precisely per call via Completions::is_incomplete whenever a real
completion context is available. This method exists only for the two
deps-lsp code paths that cannot compute that precise per-call signal
because no context has been resolved yet:
- the raw-text fallback search (
fallback_completion), which always performs a package-name lookup viacrate::Registry::searchregardless of what completion context (or lack thereof) triggered it; - the document-not-loaded early return, before any
ParseResult— and so any completion context — exists to callgenerate_completionswith.
Unlike the ecosystem-wide completions_are_incomplete() flag this method
superseded (#419, removed in #427), it never gates the primary
generate_completions response — only these two context-less fallbacks.
Default false preserves existing behavior for every ecosystem whose
package-name search is always exhaustive.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".