mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 21:05:08 +00:00
Set Durability to 'HIGH' for most inputs and third-party libraries (#12566)
This commit is contained in:
parent
fb9f566f56
commit
a2286c8e47
16 changed files with 212 additions and 126 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -2724,7 +2724,7 @@ checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "salsa"
|
name = "salsa"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=cd339fc1c9a6ea0ffb1d09bd3bffb5633f776ef3#cd339fc1c9a6ea0ffb1d09bd3bffb5633f776ef3"
|
source = "git+https://github.com/MichaReiser/salsa.git?rev=0cae5c52a3240172ef0be5c9d19e63448c53397c#0cae5c52a3240172ef0be5c9d19e63448c53397c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"boomphf",
|
"boomphf",
|
||||||
|
@ -2744,12 +2744,12 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "salsa-macro-rules"
|
name = "salsa-macro-rules"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=cd339fc1c9a6ea0ffb1d09bd3bffb5633f776ef3#cd339fc1c9a6ea0ffb1d09bd3bffb5633f776ef3"
|
source = "git+https://github.com/MichaReiser/salsa.git?rev=0cae5c52a3240172ef0be5c9d19e63448c53397c#0cae5c52a3240172ef0be5c9d19e63448c53397c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "salsa-macros"
|
name = "salsa-macros"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=cd339fc1c9a6ea0ffb1d09bd3bffb5633f776ef3#cd339fc1c9a6ea0ffb1d09bd3bffb5633f776ef3"
|
source = "git+https://github.com/MichaReiser/salsa.git?rev=0cae5c52a3240172ef0be5c9d19e63448c53397c#0cae5c52a3240172ef0be5c9d19e63448c53397c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
|
|
@ -107,7 +107,7 @@ rand = { version = "0.8.5" }
|
||||||
rayon = { version = "1.10.0" }
|
rayon = { version = "1.10.0" }
|
||||||
regex = { version = "1.10.2" }
|
regex = { version = "1.10.2" }
|
||||||
rustc-hash = { version = "2.0.0" }
|
rustc-hash = { version = "2.0.0" }
|
||||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "cd339fc1c9a6ea0ffb1d09bd3bffb5633f776ef3" }
|
salsa = { git = "https://github.com/MichaReiser/salsa.git", rev = "0cae5c52a3240172ef0be5c9d19e63448c53397c" }
|
||||||
schemars = { version = "0.8.16" }
|
schemars = { version = "0.8.16" }
|
||||||
seahash = { version = "4.1.0" }
|
seahash = { version = "4.1.0" }
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
|
|
|
@ -173,7 +173,7 @@ impl RootDatabase {
|
||||||
let package = workspace.package(self, &path);
|
let package = workspace.package(self, &path);
|
||||||
let file = system_path_to_file(self, &path);
|
let file = system_path_to_file(self, &path);
|
||||||
|
|
||||||
if let (Some(package), Some(file)) = (package, file) {
|
if let (Some(package), Ok(file)) = (package, file) {
|
||||||
package.add_file(self, file);
|
package.add_file(self, file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use salsa::Setter as _;
|
use salsa::{Durability, Setter as _};
|
||||||
use std::{collections::BTreeMap, sync::Arc};
|
use std::{collections::BTreeMap, sync::Arc};
|
||||||
|
|
||||||
use rustc_hash::{FxBuildHasher, FxHashSet};
|
use rustc_hash::{FxBuildHasher, FxHashSet};
|
||||||
|
@ -105,7 +105,9 @@ impl Workspace {
|
||||||
packages.insert(package.root.clone(), Package::from_metadata(db, package));
|
packages.insert(package.root.clone(), Package::from_metadata(db, package));
|
||||||
}
|
}
|
||||||
|
|
||||||
Workspace::new(db, metadata.root, None, packages)
|
Workspace::builder(metadata.root, None, packages)
|
||||||
|
.durability(Durability::MEDIUM)
|
||||||
|
.new(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root(self, db: &dyn Db) -> &SystemPath {
|
pub fn root(self, db: &dyn Db) -> &SystemPath {
|
||||||
|
@ -136,7 +138,9 @@ impl Workspace {
|
||||||
new_packages.insert(path, package);
|
new_packages.insert(path, package);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_package_tree(db).to(new_packages);
|
self.set_package_tree(db)
|
||||||
|
.with_durability(Durability::MEDIUM)
|
||||||
|
.to(new_packages);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
|
@ -305,20 +309,28 @@ impl Package {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_metadata(db: &dyn Db, metadata: PackageMetadata) -> Self {
|
fn from_metadata(db: &dyn Db, metadata: PackageMetadata) -> Self {
|
||||||
Self::new(db, metadata.name, metadata.root, PackageFiles::default())
|
Self::builder(metadata.name, metadata.root, PackageFiles::default())
|
||||||
|
.durability(Durability::MEDIUM)
|
||||||
|
.new(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(self, db: &mut dyn Db, metadata: PackageMetadata) {
|
fn update(self, db: &mut dyn Db, metadata: PackageMetadata) {
|
||||||
let root = self.root(db);
|
let root = self.root(db);
|
||||||
assert_eq!(root, metadata.root());
|
assert_eq!(root, metadata.root());
|
||||||
|
|
||||||
self.set_name(db).to(metadata.name);
|
if self.name(db) != metadata.name() {
|
||||||
|
self.set_name(db)
|
||||||
|
.with_durability(Durability::MEDIUM)
|
||||||
|
.to(metadata.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(db))]
|
#[tracing::instrument(level = "debug", skip(db))]
|
||||||
pub fn reload_files(self, db: &mut dyn Db) {
|
pub fn reload_files(self, db: &mut dyn Db) {
|
||||||
// Force a re-index of the files in the next revision.
|
if !self.file_set(db).is_lazy() {
|
||||||
self.set_file_set(db).to(PackageFiles::lazy());
|
// Force a re-index of the files in the next revision.
|
||||||
|
self.set_file_set(db).to(PackageFiles::lazy());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,7 +376,7 @@ fn discover_package_files(db: &dyn Db, path: &SystemPath) -> FxHashSet<File> {
|
||||||
for path in paths {
|
for path in paths {
|
||||||
// If this returns `None`, then the file was deleted between the `walk_directory` call and now.
|
// If this returns `None`, then the file was deleted between the `walk_directory` call and now.
|
||||||
// We can ignore this.
|
// We can ignore this.
|
||||||
if let Some(file) = system_path_to_file(db.upcast(), &path) {
|
if let Ok(file) = system_path_to_file(db.upcast(), &path) {
|
||||||
files.insert(file);
|
files.insert(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,10 @@ impl PackageFiles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_lazy(&self) -> bool {
|
||||||
|
matches!(*self.state.lock().unwrap(), State::Lazy)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a mutable view on the index that allows cheap in-place mutations.
|
/// Returns a mutable view on the index that allows cheap in-place mutations.
|
||||||
///
|
///
|
||||||
/// The changes are automatically written back to the database once the view is dropped.
|
/// The changes are automatically written back to the database once the view is dropped.
|
||||||
|
|
|
@ -10,7 +10,7 @@ use red_knot::watch;
|
||||||
use red_knot::watch::{directory_watcher, WorkspaceWatcher};
|
use red_knot::watch::{directory_watcher, WorkspaceWatcher};
|
||||||
use red_knot::workspace::WorkspaceMetadata;
|
use red_knot::workspace::WorkspaceMetadata;
|
||||||
use red_knot_module_resolver::{resolve_module, ModuleName};
|
use red_knot_module_resolver::{resolve_module, ModuleName};
|
||||||
use ruff_db::files::{system_path_to_file, File};
|
use ruff_db::files::{system_path_to_file, File, FileError};
|
||||||
use ruff_db::program::{Program, ProgramSettings, SearchPathSettings, TargetVersion};
|
use ruff_db::program::{Program, ProgramSettings, SearchPathSettings, TargetVersion};
|
||||||
use ruff_db::source::source_text;
|
use ruff_db::source::source_text;
|
||||||
use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf};
|
use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf};
|
||||||
|
@ -82,7 +82,7 @@ impl TestCase {
|
||||||
collected
|
collected
|
||||||
}
|
}
|
||||||
|
|
||||||
fn system_file(&self, path: impl AsRef<SystemPath>) -> Option<File> {
|
fn system_file(&self, path: impl AsRef<SystemPath>) -> Result<File, FileError> {
|
||||||
system_path_to_file(self.db(), path.as_ref())
|
system_path_to_file(self.db(), path.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ fn new_file() -> anyhow::Result<()> {
|
||||||
let bar_file = case.system_file(&bar_path).unwrap();
|
let bar_file = case.system_file(&bar_path).unwrap();
|
||||||
let foo_path = case.workspace_path("foo.py");
|
let foo_path = case.workspace_path("foo.py");
|
||||||
|
|
||||||
assert_eq!(case.system_file(&foo_path), None);
|
assert_eq!(case.system_file(&foo_path), Err(FileError::NotFound));
|
||||||
assert_eq!(&case.collect_package_files(&bar_path), &[bar_file]);
|
assert_eq!(&case.collect_package_files(&bar_path), &[bar_file]);
|
||||||
|
|
||||||
std::fs::write(foo_path.as_std_path(), "print('Hello')")?;
|
std::fs::write(foo_path.as_std_path(), "print('Hello')")?;
|
||||||
|
@ -213,7 +213,7 @@ fn new_ignored_file() -> anyhow::Result<()> {
|
||||||
let bar_file = case.system_file(&bar_path).unwrap();
|
let bar_file = case.system_file(&bar_path).unwrap();
|
||||||
let foo_path = case.workspace_path("foo.py");
|
let foo_path = case.workspace_path("foo.py");
|
||||||
|
|
||||||
assert_eq!(case.system_file(&foo_path), None);
|
assert_eq!(case.system_file(&foo_path), Err(FileError::NotFound));
|
||||||
assert_eq!(&case.collect_package_files(&bar_path), &[bar_file]);
|
assert_eq!(&case.collect_package_files(&bar_path), &[bar_file]);
|
||||||
|
|
||||||
std::fs::write(foo_path.as_std_path(), "print('Hello')")?;
|
std::fs::write(foo_path.as_std_path(), "print('Hello')")?;
|
||||||
|
@ -222,7 +222,7 @@ fn new_ignored_file() -> anyhow::Result<()> {
|
||||||
|
|
||||||
case.db_mut().apply_changes(changes);
|
case.db_mut().apply_changes(changes);
|
||||||
|
|
||||||
assert!(case.system_file(&foo_path).is_some());
|
assert!(case.system_file(&foo_path).is_ok());
|
||||||
assert_eq!(&case.collect_package_files(&bar_path), &[bar_file]);
|
assert_eq!(&case.collect_package_files(&bar_path), &[bar_file]);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -234,9 +234,7 @@ fn changed_file() -> anyhow::Result<()> {
|
||||||
let mut case = setup([("foo.py", foo_source)])?;
|
let mut case = setup([("foo.py", foo_source)])?;
|
||||||
let foo_path = case.workspace_path("foo.py");
|
let foo_path = case.workspace_path("foo.py");
|
||||||
|
|
||||||
let foo = case
|
let foo = case.system_file(&foo_path)?;
|
||||||
.system_file(&foo_path)
|
|
||||||
.ok_or_else(|| anyhow!("Foo not found"))?;
|
|
||||||
assert_eq!(source_text(case.db(), foo).as_str(), foo_source);
|
assert_eq!(source_text(case.db(), foo).as_str(), foo_source);
|
||||||
assert_eq!(&case.collect_package_files(&foo_path), &[foo]);
|
assert_eq!(&case.collect_package_files(&foo_path), &[foo]);
|
||||||
|
|
||||||
|
@ -260,9 +258,7 @@ fn changed_metadata() -> anyhow::Result<()> {
|
||||||
let mut case = setup([("foo.py", "")])?;
|
let mut case = setup([("foo.py", "")])?;
|
||||||
let foo_path = case.workspace_path("foo.py");
|
let foo_path = case.workspace_path("foo.py");
|
||||||
|
|
||||||
let foo = case
|
let foo = case.system_file(&foo_path)?;
|
||||||
.system_file(&foo_path)
|
|
||||||
.ok_or_else(|| anyhow!("Foo not found"))?;
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
foo.permissions(case.db()),
|
foo.permissions(case.db()),
|
||||||
Some(
|
Some(
|
||||||
|
@ -302,9 +298,7 @@ fn deleted_file() -> anyhow::Result<()> {
|
||||||
let mut case = setup([("foo.py", foo_source)])?;
|
let mut case = setup([("foo.py", foo_source)])?;
|
||||||
let foo_path = case.workspace_path("foo.py");
|
let foo_path = case.workspace_path("foo.py");
|
||||||
|
|
||||||
let foo = case
|
let foo = case.system_file(&foo_path)?;
|
||||||
.system_file(&foo_path)
|
|
||||||
.ok_or_else(|| anyhow!("Foo not found"))?;
|
|
||||||
|
|
||||||
assert!(foo.exists(case.db()));
|
assert!(foo.exists(case.db()));
|
||||||
assert_eq!(&case.collect_package_files(&foo_path), &[foo]);
|
assert_eq!(&case.collect_package_files(&foo_path), &[foo]);
|
||||||
|
@ -333,9 +327,7 @@ fn move_file_to_trash() -> anyhow::Result<()> {
|
||||||
let trash_path = case.root_path().join(".trash");
|
let trash_path = case.root_path().join(".trash");
|
||||||
std::fs::create_dir_all(trash_path.as_std_path())?;
|
std::fs::create_dir_all(trash_path.as_std_path())?;
|
||||||
|
|
||||||
let foo = case
|
let foo = case.system_file(&foo_path)?;
|
||||||
.system_file(&foo_path)
|
|
||||||
.ok_or_else(|| anyhow!("Foo not found"))?;
|
|
||||||
|
|
||||||
assert!(foo.exists(case.db()));
|
assert!(foo.exists(case.db()));
|
||||||
assert_eq!(&case.collect_package_files(&foo_path), &[foo]);
|
assert_eq!(&case.collect_package_files(&foo_path), &[foo]);
|
||||||
|
@ -367,7 +359,7 @@ fn move_file_to_workspace() -> anyhow::Result<()> {
|
||||||
|
|
||||||
let foo_in_workspace_path = case.workspace_path("foo.py");
|
let foo_in_workspace_path = case.workspace_path("foo.py");
|
||||||
|
|
||||||
assert!(case.system_file(&foo_path).is_some());
|
assert!(case.system_file(&foo_path).is_ok());
|
||||||
assert_eq!(&case.collect_package_files(&bar_path), &[bar]);
|
assert_eq!(&case.collect_package_files(&bar_path), &[bar]);
|
||||||
assert!(case
|
assert!(case
|
||||||
.db()
|
.db()
|
||||||
|
@ -381,9 +373,7 @@ fn move_file_to_workspace() -> anyhow::Result<()> {
|
||||||
|
|
||||||
case.db_mut().apply_changes(changes);
|
case.db_mut().apply_changes(changes);
|
||||||
|
|
||||||
let foo_in_workspace = case
|
let foo_in_workspace = case.system_file(&foo_in_workspace_path)?;
|
||||||
.system_file(&foo_in_workspace_path)
|
|
||||||
.ok_or_else(|| anyhow!("Foo not found"))?;
|
|
||||||
|
|
||||||
assert!(foo_in_workspace.exists(case.db()));
|
assert!(foo_in_workspace.exists(case.db()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -401,9 +391,7 @@ fn rename_file() -> anyhow::Result<()> {
|
||||||
let foo_path = case.workspace_path("foo.py");
|
let foo_path = case.workspace_path("foo.py");
|
||||||
let bar_path = case.workspace_path("bar.py");
|
let bar_path = case.workspace_path("bar.py");
|
||||||
|
|
||||||
let foo = case
|
let foo = case.system_file(&foo_path)?;
|
||||||
.system_file(&foo_path)
|
|
||||||
.ok_or_else(|| anyhow!("Foo not found"))?;
|
|
||||||
|
|
||||||
assert_eq!(case.collect_package_files(&foo_path), [foo]);
|
assert_eq!(case.collect_package_files(&foo_path), [foo]);
|
||||||
|
|
||||||
|
@ -415,9 +403,7 @@ fn rename_file() -> anyhow::Result<()> {
|
||||||
|
|
||||||
assert!(!foo.exists(case.db()));
|
assert!(!foo.exists(case.db()));
|
||||||
|
|
||||||
let bar = case
|
let bar = case.system_file(&bar_path)?;
|
||||||
.system_file(&bar_path)
|
|
||||||
.ok_or_else(|| anyhow!("Bar not found"))?;
|
|
||||||
|
|
||||||
assert!(bar.exists(case.db()));
|
assert!(bar.exists(case.db()));
|
||||||
assert_eq!(case.collect_package_files(&foo_path), [bar]);
|
assert_eq!(case.collect_package_files(&foo_path), [bar]);
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use camino::{Utf8Path, Utf8PathBuf};
|
use camino::{Utf8Path, Utf8PathBuf};
|
||||||
|
|
||||||
use ruff_db::files::{system_path_to_file, vendored_path_to_file, File};
|
use ruff_db::files::{system_path_to_file, vendored_path_to_file, File, FileError};
|
||||||
use ruff_db::system::{System, SystemPath, SystemPathBuf};
|
use ruff_db::system::{System, SystemPath, SystemPathBuf};
|
||||||
use ruff_db::vendored::{VendoredPath, VendoredPathBuf};
|
use ruff_db::vendored::{VendoredPath, VendoredPathBuf};
|
||||||
|
|
||||||
|
@ -68,16 +68,18 @@ impl ModulePath {
|
||||||
SearchPathInner::Extra(search_path)
|
SearchPathInner::Extra(search_path)
|
||||||
| SearchPathInner::FirstParty(search_path)
|
| SearchPathInner::FirstParty(search_path)
|
||||||
| SearchPathInner::SitePackages(search_path)
|
| SearchPathInner::SitePackages(search_path)
|
||||||
| SearchPathInner::Editable(search_path) => resolver
|
| SearchPathInner::Editable(search_path) => {
|
||||||
.system()
|
system_path_to_file(resolver.db.upcast(), search_path.join(relative_path))
|
||||||
.is_directory(&search_path.join(relative_path)),
|
== Err(FileError::IsADirectory)
|
||||||
|
}
|
||||||
SearchPathInner::StandardLibraryCustom(stdlib_root) => {
|
SearchPathInner::StandardLibraryCustom(stdlib_root) => {
|
||||||
match query_stdlib_version(Some(stdlib_root), relative_path, resolver) {
|
match query_stdlib_version(Some(stdlib_root), relative_path, resolver) {
|
||||||
TypeshedVersionsQueryResult::DoesNotExist => false,
|
TypeshedVersionsQueryResult::DoesNotExist => false,
|
||||||
TypeshedVersionsQueryResult::Exists
|
TypeshedVersionsQueryResult::Exists
|
||||||
| TypeshedVersionsQueryResult::MaybeExists => resolver
|
| TypeshedVersionsQueryResult::MaybeExists => {
|
||||||
.system()
|
system_path_to_file(resolver.db.upcast(), stdlib_root.join(relative_path))
|
||||||
.is_directory(&stdlib_root.join(relative_path)),
|
== Err(FileError::IsADirectory)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SearchPathInner::StandardLibraryVendored(stdlib_root) => {
|
SearchPathInner::StandardLibraryVendored(stdlib_root) => {
|
||||||
|
@ -105,10 +107,9 @@ impl ModulePath {
|
||||||
| SearchPathInner::SitePackages(search_path)
|
| SearchPathInner::SitePackages(search_path)
|
||||||
| SearchPathInner::Editable(search_path) => {
|
| SearchPathInner::Editable(search_path) => {
|
||||||
let absolute_path = search_path.join(relative_path);
|
let absolute_path = search_path.join(relative_path);
|
||||||
system_path_to_file(resolver.db.upcast(), absolute_path.join("__init__.py"))
|
system_path_to_file(resolver.db.upcast(), absolute_path.join("__init__.py")).is_ok()
|
||||||
.is_some()
|
|
||||||
|| system_path_to_file(resolver.db.upcast(), absolute_path.join("__init__.py"))
|
|| system_path_to_file(resolver.db.upcast(), absolute_path.join("__init__.py"))
|
||||||
.is_some()
|
.is_ok()
|
||||||
}
|
}
|
||||||
SearchPathInner::StandardLibraryCustom(search_path) => {
|
SearchPathInner::StandardLibraryCustom(search_path) => {
|
||||||
match query_stdlib_version(Some(search_path), relative_path, resolver) {
|
match query_stdlib_version(Some(search_path), relative_path, resolver) {
|
||||||
|
@ -118,7 +119,7 @@ impl ModulePath {
|
||||||
resolver.db.upcast(),
|
resolver.db.upcast(),
|
||||||
search_path.join(relative_path).join("__init__.pyi"),
|
search_path.join(relative_path).join("__init__.pyi"),
|
||||||
)
|
)
|
||||||
.is_some(),
|
.is_ok(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SearchPathInner::StandardLibraryVendored(search_path) => {
|
SearchPathInner::StandardLibraryVendored(search_path) => {
|
||||||
|
@ -145,14 +146,14 @@ impl ModulePath {
|
||||||
| SearchPathInner::FirstParty(search_path)
|
| SearchPathInner::FirstParty(search_path)
|
||||||
| SearchPathInner::SitePackages(search_path)
|
| SearchPathInner::SitePackages(search_path)
|
||||||
| SearchPathInner::Editable(search_path) => {
|
| SearchPathInner::Editable(search_path) => {
|
||||||
system_path_to_file(db, search_path.join(relative_path))
|
system_path_to_file(db, search_path.join(relative_path)).ok()
|
||||||
}
|
}
|
||||||
SearchPathInner::StandardLibraryCustom(stdlib_root) => {
|
SearchPathInner::StandardLibraryCustom(stdlib_root) => {
|
||||||
match query_stdlib_version(Some(stdlib_root), relative_path, resolver) {
|
match query_stdlib_version(Some(stdlib_root), relative_path, resolver) {
|
||||||
TypeshedVersionsQueryResult::DoesNotExist => None,
|
TypeshedVersionsQueryResult::DoesNotExist => None,
|
||||||
TypeshedVersionsQueryResult::Exists
|
TypeshedVersionsQueryResult::Exists
|
||||||
| TypeshedVersionsQueryResult::MaybeExists => {
|
| TypeshedVersionsQueryResult::MaybeExists => {
|
||||||
system_path_to_file(db, stdlib_root.join(relative_path))
|
system_path_to_file(db, stdlib_root.join(relative_path)).ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +162,7 @@ impl ModulePath {
|
||||||
TypeshedVersionsQueryResult::DoesNotExist => None,
|
TypeshedVersionsQueryResult::DoesNotExist => None,
|
||||||
TypeshedVersionsQueryResult::Exists
|
TypeshedVersionsQueryResult::Exists
|
||||||
| TypeshedVersionsQueryResult::MaybeExists => {
|
| TypeshedVersionsQueryResult::MaybeExists => {
|
||||||
vendored_path_to_file(db, stdlib_root.join(relative_path))
|
vendored_path_to_file(db, stdlib_root.join(relative_path)).ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,11 +302,15 @@ pub(crate) enum SearchPathValidationError {
|
||||||
/// (This is only relevant for stdlib search paths.)
|
/// (This is only relevant for stdlib search paths.)
|
||||||
NoStdlibSubdirectory(SystemPathBuf),
|
NoStdlibSubdirectory(SystemPathBuf),
|
||||||
|
|
||||||
/// The path provided by the user is a directory,
|
/// The typeshed path provided by the user is a directory,
|
||||||
/// but no `stdlib/VERSIONS` file exists.
|
/// but no `stdlib/VERSIONS` file exists.
|
||||||
/// (This is only relevant for stdlib search paths.)
|
/// (This is only relevant for stdlib search paths.)
|
||||||
NoVersionsFile(SystemPathBuf),
|
NoVersionsFile(SystemPathBuf),
|
||||||
|
|
||||||
|
/// `stdlib/VERSIONS` is a directory.
|
||||||
|
/// (This is only relevant for stdlib search paths.)
|
||||||
|
VersionsIsADirectory(SystemPathBuf),
|
||||||
|
|
||||||
/// The path provided by the user is a directory,
|
/// The path provided by the user is a directory,
|
||||||
/// and a `stdlib/VERSIONS` file exists, but it fails to parse.
|
/// and a `stdlib/VERSIONS` file exists, but it fails to parse.
|
||||||
/// (This is only relevant for stdlib search paths.)
|
/// (This is only relevant for stdlib search paths.)
|
||||||
|
@ -320,6 +325,7 @@ impl fmt::Display for SearchPathValidationError {
|
||||||
write!(f, "The directory at {path} has no `stdlib/` subdirectory")
|
write!(f, "The directory at {path} has no `stdlib/` subdirectory")
|
||||||
}
|
}
|
||||||
Self::NoVersionsFile(path) => write!(f, "Expected a file at {path}/stldib/VERSIONS"),
|
Self::NoVersionsFile(path) => write!(f, "Expected a file at {path}/stldib/VERSIONS"),
|
||||||
|
Self::VersionsIsADirectory(path) => write!(f, "{path}/stldib/VERSIONS is a directory."),
|
||||||
Self::VersionsParseError(underlying_error) => underlying_error.fmt(f),
|
Self::VersionsParseError(underlying_error) => underlying_error.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,10 +414,13 @@ impl SearchPath {
|
||||||
typeshed.to_path_buf(),
|
typeshed.to_path_buf(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let Some(typeshed_versions) = system_path_to_file(db.upcast(), stdlib.join("VERSIONS"))
|
let typeshed_versions =
|
||||||
else {
|
system_path_to_file(db.upcast(), stdlib.join("VERSIONS")).map_err(|err| match err {
|
||||||
return Err(SearchPathValidationError::NoVersionsFile(typeshed));
|
FileError::NotFound => SearchPathValidationError::NoVersionsFile(typeshed),
|
||||||
};
|
FileError::IsADirectory => {
|
||||||
|
SearchPathValidationError::VersionsIsADirectory(typeshed)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
crate::typeshed::parse_typeshed_versions(db, typeshed_versions)
|
crate::typeshed::parse_typeshed_versions(db, typeshed_versions)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_err(|validation_error| {
|
.map_err(|validation_error| {
|
||||||
|
|
|
@ -489,6 +489,7 @@ fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, File, Mod
|
||||||
if is_builtin_module && !search_path.is_standard_library() {
|
if is_builtin_module && !search_path.is_standard_library() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut components = name.components();
|
let mut components = name.components();
|
||||||
let module_name = components.next_back()?;
|
let module_name = components.next_back()?;
|
||||||
|
|
||||||
|
@ -1282,6 +1283,7 @@ mod tests {
|
||||||
db.memory_file_system()
|
db.memory_file_system()
|
||||||
.remove_directory(foo_init_path.parent().unwrap())?;
|
.remove_directory(foo_init_path.parent().unwrap())?;
|
||||||
File::sync_path(&mut db, &foo_init_path);
|
File::sync_path(&mut db, &foo_init_path);
|
||||||
|
File::sync_path(&mut db, foo_init_path.parent().unwrap());
|
||||||
|
|
||||||
let foo_module = resolve_module(&db, foo_module_name).expect("Foo module to resolve");
|
let foo_module = resolve_module(&db, foo_module_name).expect("Foo module to resolve");
|
||||||
assert_eq!(&src.join("foo.py"), foo_module.file().path(&db));
|
assert_eq!(&src.join("foo.py"), foo_module.file().path(&db));
|
||||||
|
@ -1312,7 +1314,7 @@ mod tests {
|
||||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||||
assert_eq!(functools_module.search_path(), &stdlib);
|
assert_eq!(functools_module.search_path(), &stdlib);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(functools_module.file()),
|
Ok(functools_module.file()),
|
||||||
system_path_to_file(&db, &stdlib_functools_path)
|
system_path_to_file(&db, &stdlib_functools_path)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1332,7 +1334,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(functools_module.search_path(), &stdlib);
|
assert_eq!(functools_module.search_path(), &stdlib);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(functools_module.file()),
|
Ok(functools_module.file()),
|
||||||
system_path_to_file(&db, &stdlib_functools_path)
|
system_path_to_file(&db, &stdlib_functools_path)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1358,7 +1360,7 @@ mod tests {
|
||||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||||
assert_eq!(functools_module.search_path(), &stdlib);
|
assert_eq!(functools_module.search_path(), &stdlib);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(functools_module.file()),
|
Ok(functools_module.file()),
|
||||||
system_path_to_file(&db, stdlib.join("functools.pyi"))
|
system_path_to_file(&db, stdlib.join("functools.pyi"))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1369,7 +1371,7 @@ mod tests {
|
||||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||||
assert_eq!(functools_module.search_path(), &src);
|
assert_eq!(functools_module.search_path(), &src);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(functools_module.file()),
|
Ok(functools_module.file()),
|
||||||
system_path_to_file(&db, &src_functools_path)
|
system_path_to_file(&db, &src_functools_path)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1400,7 +1402,7 @@ mod tests {
|
||||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||||
assert_eq!(functools_module.search_path(), &src);
|
assert_eq!(functools_module.search_path(), &src);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(functools_module.file()),
|
Ok(functools_module.file()),
|
||||||
system_path_to_file(&db, &src_functools_path)
|
system_path_to_file(&db, &src_functools_path)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1413,7 +1415,7 @@ mod tests {
|
||||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||||
assert_eq!(functools_module.search_path(), &stdlib);
|
assert_eq!(functools_module.search_path(), &stdlib);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(functools_module.file()),
|
Ok(functools_module.file()),
|
||||||
system_path_to_file(&db, stdlib.join("functools.pyi"))
|
system_path_to_file(&db, stdlib.join("functools.pyi"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use ruff_db::program::TargetVersion;
|
use ruff_db::program::TargetVersion;
|
||||||
use ruff_db::system::System;
|
|
||||||
use ruff_db::vendored::VendoredFileSystem;
|
use ruff_db::vendored::VendoredFileSystem;
|
||||||
|
|
||||||
use crate::db::Db;
|
use crate::db::Db;
|
||||||
|
@ -20,10 +19,6 @@ impl<'db> ResolverState<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn system(&self) -> &dyn System {
|
|
||||||
self.db.system()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn vendored(&self) -> &VendoredFileSystem {
|
pub(crate) fn vendored(&self) -> &VendoredFileSystem {
|
||||||
self.db.vendored()
|
self.db.vendored()
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ impl<'db> LazyTypeshedVersions<'db> {
|
||||||
} else {
|
} else {
|
||||||
return &VENDORED_VERSIONS;
|
return &VENDORED_VERSIONS;
|
||||||
};
|
};
|
||||||
let Some(versions_file) = system_path_to_file(db.upcast(), &versions_path) else {
|
let Ok(versions_file) = system_path_to_file(db.upcast(), &versions_path) else {
|
||||||
todo!(
|
todo!(
|
||||||
"Still need to figure out how to handle VERSIONS files being deleted \
|
"Still need to figure out how to handle VERSIONS files being deleted \
|
||||||
from custom typeshed directories! Expected a file to exist at {versions_path}"
|
from custom typeshed directories! Expected a file to exist at {versions_path}"
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
use std::fmt::Formatter;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use countme::Count;
|
use countme::Count;
|
||||||
use dashmap::mapref::entry::Entry;
|
use dashmap::mapref::entry::Entry;
|
||||||
use salsa::Setter;
|
use salsa::{Durability, Setter};
|
||||||
|
|
||||||
pub use file_root::{FileRoot, FileRootKind};
|
pub use file_root::{FileRoot, FileRootKind};
|
||||||
pub use path::FilePath;
|
pub use path::FilePath;
|
||||||
|
@ -13,28 +14,35 @@ use crate::files::file_root::FileRoots;
|
||||||
use crate::files::private::FileStatus;
|
use crate::files::private::FileStatus;
|
||||||
use crate::system::{Metadata, SystemPath, SystemPathBuf, SystemVirtualPath, SystemVirtualPathBuf};
|
use crate::system::{Metadata, SystemPath, SystemPathBuf, SystemVirtualPath, SystemVirtualPathBuf};
|
||||||
use crate::vendored::{VendoredPath, VendoredPathBuf};
|
use crate::vendored::{VendoredPath, VendoredPathBuf};
|
||||||
use crate::{Db, FxDashMap};
|
use crate::{vendored, Db, FxDashMap};
|
||||||
|
|
||||||
mod file_root;
|
mod file_root;
|
||||||
mod path;
|
mod path;
|
||||||
|
|
||||||
/// Interns a file system path and returns a salsa `File` ingredient.
|
/// Interns a file system path and returns a salsa `File` ingredient.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the path doesn't exist, isn't accessible, or if the path points to a directory.
|
/// Returns `Err` if the path doesn't exist, isn't accessible, or if the path points to a directory.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn system_path_to_file(db: &dyn Db, path: impl AsRef<SystemPath>) -> Option<File> {
|
pub fn system_path_to_file(db: &dyn Db, path: impl AsRef<SystemPath>) -> Result<File, FileError> {
|
||||||
let file = db.files().system(db, path.as_ref());
|
let file = db.files().system(db, path.as_ref());
|
||||||
|
|
||||||
// It's important that `vfs.file_system` creates a `VfsFile` even for files that don't exist or don't
|
// It's important that `vfs.file_system` creates a `VfsFile` even for files that don't exist or don't
|
||||||
// exist anymore so that Salsa can track that the caller of this function depends on the existence of
|
// exist anymore so that Salsa can track that the caller of this function depends on the existence of
|
||||||
// that file. This function filters out files that don't exist, but Salsa will know that it must
|
// that file. This function filters out files that don't exist, but Salsa will know that it must
|
||||||
// re-run the calling query whenever the `file`'s status changes (because of the `.status` call here).
|
// re-run the calling query whenever the `file`'s status changes (because of the `.status` call here).
|
||||||
file.exists(db).then_some(file)
|
match file.status(db) {
|
||||||
|
FileStatus::Exists => Ok(file),
|
||||||
|
FileStatus::IsADirectory => Err(FileError::IsADirectory),
|
||||||
|
FileStatus::NotFound => Err(FileError::NotFound),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interns a vendored file path. Returns `Some` if the vendored file for `path` exists and `None` otherwise.
|
/// Interns a vendored file path. Returns `Some` if the vendored file for `path` exists and `None` otherwise.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn vendored_path_to_file(db: &dyn Db, path: impl AsRef<VendoredPath>) -> Option<File> {
|
pub fn vendored_path_to_file(
|
||||||
|
db: &dyn Db,
|
||||||
|
path: impl AsRef<VendoredPath>,
|
||||||
|
) -> Result<File, FileError> {
|
||||||
db.files().vendored(db, path.as_ref())
|
db.files().vendored(db, path.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +76,7 @@ impl Files {
|
||||||
/// For a non-existing file, creates a new salsa [`File`] ingredient and stores it for future lookups.
|
/// For a non-existing file, creates a new salsa [`File`] ingredient and stores it for future lookups.
|
||||||
///
|
///
|
||||||
/// The operation always succeeds even if the path doesn't exist on disk, isn't accessible or if the path points to a directory.
|
/// The operation always succeeds even if the path doesn't exist on disk, isn't accessible or if the path points to a directory.
|
||||||
/// In these cases, a file with status [`FileStatus::Deleted`] is returned.
|
/// In these cases, a file with status [`FileStatus::NotFound`] is returned.
|
||||||
#[tracing::instrument(level = "trace", skip(self, db))]
|
#[tracing::instrument(level = "trace", skip(self, db))]
|
||||||
fn system(&self, db: &dyn Db, path: &SystemPath) -> File {
|
fn system(&self, db: &dyn Db, path: &SystemPath) -> File {
|
||||||
let absolute = SystemPath::absolute(path, db.system().current_directory());
|
let absolute = SystemPath::absolute(path, db.system().current_directory());
|
||||||
|
@ -78,27 +86,32 @@ impl Files {
|
||||||
.system_by_path
|
.system_by_path
|
||||||
.entry(absolute.clone())
|
.entry(absolute.clone())
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
// TODO: Set correct durability according to source root.
|
|
||||||
let metadata = db.system().path_metadata(path);
|
let metadata = db.system().path_metadata(path);
|
||||||
|
let durability = self
|
||||||
|
.root(db, path)
|
||||||
|
.map_or(Durability::default(), |root| root.durability(db));
|
||||||
|
|
||||||
match metadata {
|
let (permissions, revision, status) = match metadata {
|
||||||
Ok(metadata) if metadata.file_type().is_file() => File::new(
|
Ok(metadata) if metadata.file_type().is_file() => (
|
||||||
db,
|
|
||||||
FilePath::System(absolute),
|
|
||||||
metadata.permissions(),
|
metadata.permissions(),
|
||||||
metadata.revision(),
|
metadata.revision(),
|
||||||
FileStatus::Exists,
|
FileStatus::Exists,
|
||||||
Count::default(),
|
|
||||||
),
|
),
|
||||||
_ => File::new(
|
Ok(metadata) if metadata.file_type().is_directory() => {
|
||||||
db,
|
(None, FileRevision::zero(), FileStatus::IsADirectory)
|
||||||
FilePath::System(absolute),
|
}
|
||||||
None,
|
_ => (None, FileRevision::zero(), FileStatus::NotFound),
|
||||||
FileRevision::zero(),
|
};
|
||||||
FileStatus::Deleted,
|
|
||||||
Count::default(),
|
File::builder(
|
||||||
),
|
FilePath::System(absolute),
|
||||||
}
|
permissions,
|
||||||
|
revision,
|
||||||
|
status,
|
||||||
|
Count::default(),
|
||||||
|
)
|
||||||
|
.durability(durability)
|
||||||
|
.new(db)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,20 +127,27 @@ impl Files {
|
||||||
/// Looks up a vendored file by its path. Returns `Some` if a vendored file for the given path
|
/// Looks up a vendored file by its path. Returns `Some` if a vendored file for the given path
|
||||||
/// exists and `None` otherwise.
|
/// exists and `None` otherwise.
|
||||||
#[tracing::instrument(level = "trace", skip(self, db))]
|
#[tracing::instrument(level = "trace", skip(self, db))]
|
||||||
fn vendored(&self, db: &dyn Db, path: &VendoredPath) -> Option<File> {
|
fn vendored(&self, db: &dyn Db, path: &VendoredPath) -> Result<File, FileError> {
|
||||||
let file = match self.inner.vendored_by_path.entry(path.to_path_buf()) {
|
let file = match self.inner.vendored_by_path.entry(path.to_path_buf()) {
|
||||||
Entry::Occupied(entry) => *entry.get(),
|
Entry::Occupied(entry) => *entry.get(),
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
let metadata = db.vendored().metadata(path).ok()?;
|
let metadata = match db.vendored().metadata(path) {
|
||||||
|
Ok(metadata) => match metadata.kind() {
|
||||||
|
vendored::FileType::File => metadata,
|
||||||
|
vendored::FileType::Directory => return Err(FileError::IsADirectory),
|
||||||
|
},
|
||||||
|
Err(_) => return Err(FileError::NotFound),
|
||||||
|
};
|
||||||
|
|
||||||
let file = File::new(
|
let file = File::builder(
|
||||||
db,
|
|
||||||
FilePath::Vendored(path.to_path_buf()),
|
FilePath::Vendored(path.to_path_buf()),
|
||||||
Some(0o444),
|
Some(0o444),
|
||||||
metadata.revision(),
|
metadata.revision(),
|
||||||
FileStatus::Exists,
|
FileStatus::Exists,
|
||||||
Count::default(),
|
Count::default(),
|
||||||
);
|
)
|
||||||
|
.durability(Durability::HIGH)
|
||||||
|
.new(db);
|
||||||
|
|
||||||
entry.insert(file);
|
entry.insert(file);
|
||||||
|
|
||||||
|
@ -135,7 +155,7 @@ impl Files {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(file)
|
Ok(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Looks up a virtual file by its `path`.
|
/// Looks up a virtual file by its `path`.
|
||||||
|
@ -210,8 +230,7 @@ impl Files {
|
||||||
let inner = Arc::clone(&db.files().inner);
|
let inner = Arc::clone(&db.files().inner);
|
||||||
for entry in inner.system_by_path.iter_mut() {
|
for entry in inner.system_by_path.iter_mut() {
|
||||||
if entry.key().starts_with(&path) {
|
if entry.key().starts_with(&path) {
|
||||||
let file = entry.value();
|
File::sync_system_path(db, entry.key(), Some(*entry.value()));
|
||||||
file.sync(db);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +238,9 @@ impl Files {
|
||||||
|
|
||||||
for root in roots.all() {
|
for root in roots.all() {
|
||||||
if root.path(db).starts_with(&path) {
|
if root.path(db).starts_with(&path) {
|
||||||
root.set_revision(db).to(FileRevision::now());
|
root.set_revision(db)
|
||||||
|
.with_durability(Durability::HIGH)
|
||||||
|
.to(FileRevision::now());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,14 +257,15 @@ impl Files {
|
||||||
pub fn sync_all(db: &mut dyn Db) {
|
pub fn sync_all(db: &mut dyn Db) {
|
||||||
let inner = Arc::clone(&db.files().inner);
|
let inner = Arc::clone(&db.files().inner);
|
||||||
for entry in inner.system_by_path.iter_mut() {
|
for entry in inner.system_by_path.iter_mut() {
|
||||||
let file = entry.value();
|
File::sync_system_path(db, entry.key(), Some(*entry.value()));
|
||||||
file.sync(db);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let roots = inner.roots.read().unwrap();
|
let roots = inner.roots.read().unwrap();
|
||||||
|
|
||||||
for root in roots.all() {
|
for root in roots.all() {
|
||||||
root.set_revision(db).to(FileRevision::now());
|
root.set_revision(db)
|
||||||
|
.with_durability(Durability::HIGH)
|
||||||
|
.to(FileRevision::now());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,6 +357,7 @@ impl File {
|
||||||
#[tracing::instrument(level = "debug", skip(db))]
|
#[tracing::instrument(level = "debug", skip(db))]
|
||||||
pub fn sync_path(db: &mut dyn Db, path: &SystemPath) {
|
pub fn sync_path(db: &mut dyn Db, path: &SystemPath) {
|
||||||
let absolute = SystemPath::absolute(path, db.system().current_directory());
|
let absolute = SystemPath::absolute(path, db.system().current_directory());
|
||||||
|
Files::touch_root(db, &absolute);
|
||||||
Self::sync_system_path(db, &absolute, None);
|
Self::sync_system_path(db, &absolute, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,6 +368,7 @@ impl File {
|
||||||
|
|
||||||
match path {
|
match path {
|
||||||
FilePath::System(system) => {
|
FilePath::System(system) => {
|
||||||
|
Files::touch_root(db, &system);
|
||||||
Self::sync_system_path(db, &system, Some(self));
|
Self::sync_system_path(db, &system, Some(self));
|
||||||
}
|
}
|
||||||
FilePath::Vendored(_) => {
|
FilePath::Vendored(_) => {
|
||||||
|
@ -357,34 +381,56 @@ impl File {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync_system_path(db: &mut dyn Db, path: &SystemPath, file: Option<File>) {
|
fn sync_system_path(db: &mut dyn Db, path: &SystemPath, file: Option<File>) {
|
||||||
Files::touch_root(db, path);
|
|
||||||
let Some(file) = file.or_else(|| db.files().try_system(db, path)) else {
|
let Some(file) = file.or_else(|| db.files().try_system(db, path)) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let metadata = db.system().path_metadata(path);
|
let metadata = db.system().path_metadata(path);
|
||||||
Self::sync_impl(db, metadata, file);
|
let durability = db.files().root(db, path).map(|root| root.durability(db));
|
||||||
|
Self::sync_impl(db, metadata, file, durability);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync_system_virtual_path(db: &mut dyn Db, path: &SystemVirtualPath, file: File) {
|
fn sync_system_virtual_path(db: &mut dyn Db, path: &SystemVirtualPath, file: File) {
|
||||||
let metadata = db.system().virtual_path_metadata(path);
|
let metadata = db.system().virtual_path_metadata(path);
|
||||||
Self::sync_impl(db, metadata, file);
|
Self::sync_impl(db, metadata, file, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Private method providing the implementation for [`Self::sync_system_path`] and
|
/// Private method providing the implementation for [`Self::sync_system_path`] and
|
||||||
/// [`Self::sync_system_virtual_path`].
|
/// [`Self::sync_system_virtual_path`].
|
||||||
fn sync_impl(db: &mut dyn Db, metadata: crate::system::Result<Metadata>, file: File) {
|
fn sync_impl(
|
||||||
|
db: &mut dyn Db,
|
||||||
|
metadata: crate::system::Result<Metadata>,
|
||||||
|
file: File,
|
||||||
|
durability: Option<Durability>,
|
||||||
|
) {
|
||||||
let (status, revision, permission) = match metadata {
|
let (status, revision, permission) = match metadata {
|
||||||
Ok(metadata) if metadata.file_type().is_file() => (
|
Ok(metadata) if metadata.file_type().is_file() => (
|
||||||
FileStatus::Exists,
|
FileStatus::Exists,
|
||||||
metadata.revision(),
|
metadata.revision(),
|
||||||
metadata.permissions(),
|
metadata.permissions(),
|
||||||
),
|
),
|
||||||
_ => (FileStatus::Deleted, FileRevision::zero(), None),
|
Ok(metadata) if metadata.file_type().is_directory() => {
|
||||||
|
(FileStatus::IsADirectory, FileRevision::zero(), None)
|
||||||
|
}
|
||||||
|
_ => (FileStatus::NotFound, FileRevision::zero(), None),
|
||||||
};
|
};
|
||||||
|
|
||||||
file.set_status(db).to(status);
|
let durability = durability.unwrap_or_default();
|
||||||
file.set_revision(db).to(revision);
|
|
||||||
file.set_permissions(db).to(permission);
|
if file.status(db) != status {
|
||||||
|
file.set_status(db).with_durability(durability).to(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if file.revision(db) != revision {
|
||||||
|
file.set_revision(db)
|
||||||
|
.with_durability(durability)
|
||||||
|
.to(revision);
|
||||||
|
}
|
||||||
|
|
||||||
|
if file.permissions(db) != permission {
|
||||||
|
file.set_permissions(db)
|
||||||
|
.with_durability(durability)
|
||||||
|
.to(permission);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the file exists.
|
/// Returns `true` if the file exists.
|
||||||
|
@ -401,15 +447,35 @@ mod private {
|
||||||
/// The file exists.
|
/// The file exists.
|
||||||
Exists,
|
Exists,
|
||||||
|
|
||||||
/// The file was deleted, didn't exist to begin with or the path isn't a file.
|
/// The path isn't a file and instead points to a directory.
|
||||||
Deleted,
|
IsADirectory,
|
||||||
|
|
||||||
|
/// The path doesn't exist, isn't accessible, or no longer exists.
|
||||||
|
NotFound,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum FileError {
|
||||||
|
IsADirectory,
|
||||||
|
NotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for FileError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
FileError::IsADirectory => f.write_str("Is a directory"),
|
||||||
|
FileError::NotFound => f.write_str("Not found"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for FileError {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::file_revision::FileRevision;
|
use crate::file_revision::FileRevision;
|
||||||
use crate::files::{system_path_to_file, vendored_path_to_file};
|
use crate::files::{system_path_to_file, vendored_path_to_file, FileError};
|
||||||
use crate::system::DbWithTestSystem;
|
use crate::system::DbWithTestSystem;
|
||||||
use crate::tests::TestDb;
|
use crate::tests::TestDb;
|
||||||
use crate::vendored::tests::VendoredFileSystemBuilder;
|
use crate::vendored::tests::VendoredFileSystemBuilder;
|
||||||
|
@ -435,7 +501,7 @@ mod tests {
|
||||||
|
|
||||||
let test = system_path_to_file(&db, "test.py");
|
let test = system_path_to_file(&db, "test.py");
|
||||||
|
|
||||||
assert_eq!(test, None);
|
assert_eq!(test, Err(FileError::NotFound));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -477,6 +543,9 @@ mod tests {
|
||||||
fn stubbed_vendored_file_non_existing() {
|
fn stubbed_vendored_file_non_existing() {
|
||||||
let db = TestDb::new();
|
let db = TestDb::new();
|
||||||
|
|
||||||
assert_eq!(vendored_path_to_file(&db, "test.py"), None);
|
assert_eq!(
|
||||||
|
vendored_path_to_file(&db, "test.py"),
|
||||||
|
Err(FileError::NotFound)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
|
|
||||||
use path_slash::PathExt;
|
use path_slash::PathExt;
|
||||||
|
use salsa::Durability;
|
||||||
|
|
||||||
use crate::file_revision::FileRevision;
|
use crate::file_revision::FileRevision;
|
||||||
use crate::system::{SystemPath, SystemPathBuf};
|
use crate::system::{SystemPath, SystemPathBuf};
|
||||||
|
@ -83,7 +84,9 @@ impl FileRoots {
|
||||||
let mut route = normalized_path.replace('{', "{{").replace('}', "}}");
|
let mut route = normalized_path.replace('{', "{{").replace('}', "}}");
|
||||||
|
|
||||||
// Insert a new source root
|
// Insert a new source root
|
||||||
let root = FileRoot::new(db, path, kind, FileRevision::now());
|
let root = FileRoot::builder(path, kind, FileRevision::now())
|
||||||
|
.durability(Durability::HIGH)
|
||||||
|
.new(db);
|
||||||
|
|
||||||
// Insert a path that matches the root itself
|
// Insert a path that matches the root itself
|
||||||
self.by_path.insert(route.clone(), root).unwrap();
|
self.by_path.insert(route.clone(), root).unwrap();
|
||||||
|
|
|
@ -95,8 +95,8 @@ impl FilePath {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_file(&self, db: &dyn Db) -> Option<File> {
|
pub fn to_file(&self, db: &dyn Db) -> Option<File> {
|
||||||
match self {
|
match self {
|
||||||
FilePath::System(path) => system_path_to_file(db, path),
|
FilePath::System(path) => system_path_to_file(db, path).ok(),
|
||||||
FilePath::Vendored(path) => vendored_path_to_file(db, path),
|
FilePath::Vendored(path) => vendored_path_to_file(db, path).ok(),
|
||||||
FilePath::SystemVirtual(_) => None,
|
FilePath::SystemVirtual(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::{system::SystemPathBuf, Db};
|
use crate::{system::SystemPathBuf, Db};
|
||||||
|
use salsa::Durability;
|
||||||
|
|
||||||
#[salsa::input(singleton)]
|
#[salsa::input(singleton)]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
|
@ -10,7 +11,9 @@ pub struct Program {
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
pub fn from_settings(db: &dyn Db, settings: ProgramSettings) -> Self {
|
pub fn from_settings(db: &dyn Db, settings: ProgramSettings) -> Self {
|
||||||
Program::new(db, settings.target_version, settings.search_paths)
|
Program::builder(settings.target_version, settings.search_paths)
|
||||||
|
.durability(Durability::HIGH)
|
||||||
|
.new(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -207,7 +207,9 @@ impl MemoryFileSystem {
|
||||||
|
|
||||||
let normalized = self.normalize_path(path.as_ref());
|
let normalized = self.normalize_path(path.as_ref());
|
||||||
|
|
||||||
get_or_create_file(&mut by_path, &normalized)?.content = content.to_string();
|
let file = get_or_create_file(&mut by_path, &normalized)?;
|
||||||
|
file.content = content.to_string();
|
||||||
|
file.last_modified = FileTime::now();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
use std::any::Any;
|
||||||
|
use std::panic::RefUnwindSafe;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ruff_notebook::{Notebook, NotebookError};
|
use ruff_notebook::{Notebook, NotebookError};
|
||||||
use ruff_python_trivia::textwrap;
|
use ruff_python_trivia::textwrap;
|
||||||
|
|
||||||
|
@ -6,9 +10,6 @@ use crate::system::{
|
||||||
DirectoryEntry, MemoryFileSystem, Metadata, Result, System, SystemPath, SystemVirtualPath,
|
DirectoryEntry, MemoryFileSystem, Metadata, Result, System, SystemPath, SystemVirtualPath,
|
||||||
};
|
};
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
use std::any::Any;
|
|
||||||
use std::panic::RefUnwindSafe;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::walk_directory::WalkDirectoryBuilder;
|
use super::walk_directory::WalkDirectoryBuilder;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue