mirror of https://github.com/astral-sh/ruff
Split out `discover_in` from `ProjectMetadata::discover`
This commit is contained in:
parent
efb23b01af
commit
1e77da4d17
|
|
@ -1,7 +1,9 @@
|
|||
use configuration_file::{ConfigurationFile, ConfigurationFileError};
|
||||
use ruff_db::system::walk_directory::WalkState;
|
||||
use ruff_db::system::{System, SystemPath, SystemPathBuf};
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
use ruff_python_ast::name::Name;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use ty_combine::Combine;
|
||||
|
|
@ -141,6 +143,64 @@ impl ProjectMetadata {
|
|||
let mut closest_project: Option<ProjectMetadata> = None;
|
||||
|
||||
for project_root in path.ancestors() {
|
||||
let Some(discovered) = Self::discover_in(project_root, system)? else {
|
||||
continue;
|
||||
};
|
||||
|
||||
match discovered {
|
||||
DiscoveredProject::PyProject {
|
||||
has_ty_section: true,
|
||||
metadata,
|
||||
}
|
||||
| DiscoveredProject::Ty { metadata } => {
|
||||
tracing::debug!("Found project at '{}'", project_root);
|
||||
|
||||
return Ok(metadata);
|
||||
}
|
||||
DiscoveredProject::PyProject { metadata, .. } => {
|
||||
// Not a project itself, keep looking for an enclosing project.
|
||||
if closest_project.is_none() {
|
||||
closest_project = Some(metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No project found, but maybe a pyproject.toml was found.
|
||||
let metadata = if let Some(closest_project) = closest_project {
|
||||
tracing::debug!(
|
||||
"Project without `tool.ty` section: '{}'",
|
||||
closest_project.root()
|
||||
);
|
||||
|
||||
closest_project
|
||||
} else {
|
||||
tracing::debug!(
|
||||
"The ancestor directories contain no `pyproject.toml`. Falling back to a virtual project."
|
||||
);
|
||||
|
||||
// Create a project with a default configuration
|
||||
Self::new(
|
||||
path.file_name().unwrap_or("root").into(),
|
||||
path.to_path_buf(),
|
||||
)
|
||||
};
|
||||
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
fn discover_in(
|
||||
project_root: &SystemPath,
|
||||
system: &dyn System,
|
||||
) -> Result<Option<DiscoveredProject>, ProjectMetadataError> {
|
||||
tracing::debug!("Searching for a project in '{project_root}'");
|
||||
|
||||
if !system.is_directory(project_root) {
|
||||
return Err(ProjectMetadataError::NotADirectory(
|
||||
project_root.to_path_buf(),
|
||||
));
|
||||
}
|
||||
|
||||
let pyproject_path = project_root.join("pyproject.toml");
|
||||
|
||||
let pyproject = if let Ok(pyproject_str) = system.read_to_string(&pyproject_path) {
|
||||
|
|
@ -195,20 +255,6 @@ impl ProjectMetadata {
|
|||
.as_ref()
|
||||
.and_then(|pyproject| pyproject.project.as_ref()),
|
||||
)
|
||||
.map_err(|err| {
|
||||
ProjectMetadataError::InvalidRequiresPythonConstraint {
|
||||
source: err,
|
||||
path: pyproject_path,
|
||||
}
|
||||
})?;
|
||||
|
||||
return Ok(metadata);
|
||||
}
|
||||
|
||||
if let Some(pyproject) = pyproject {
|
||||
let has_ty_section = pyproject.ty().is_some();
|
||||
let metadata =
|
||||
ProjectMetadata::from_pyproject(pyproject, project_root.to_path_buf())
|
||||
.map_err(
|
||||
|err| ProjectMetadataError::InvalidRequiresPythonConstraint {
|
||||
source: err,
|
||||
|
|
@ -216,40 +262,26 @@ impl ProjectMetadata {
|
|||
},
|
||||
)?;
|
||||
|
||||
if has_ty_section {
|
||||
tracing::debug!("Found project at '{}'", project_root);
|
||||
|
||||
return Ok(metadata);
|
||||
return Ok(Some(DiscoveredProject::Ty { metadata }));
|
||||
}
|
||||
|
||||
// Not a project itself, keep looking for an enclosing project.
|
||||
if closest_project.is_none() {
|
||||
closest_project = Some(metadata);
|
||||
}
|
||||
if let Some(pyproject) = pyproject {
|
||||
let has_ty_section = pyproject.ty().is_some();
|
||||
let metadata = ProjectMetadata::from_pyproject(pyproject, project_root.to_path_buf())
|
||||
.map_err(|err| {
|
||||
ProjectMetadataError::InvalidRequiresPythonConstraint {
|
||||
source: err,
|
||||
path: pyproject_path,
|
||||
}
|
||||
})?;
|
||||
|
||||
return Ok(Some(DiscoveredProject::PyProject {
|
||||
has_ty_section,
|
||||
metadata,
|
||||
}));
|
||||
}
|
||||
|
||||
// No project found, but maybe a pyproject.toml was found.
|
||||
let metadata = if let Some(closest_project) = closest_project {
|
||||
tracing::debug!(
|
||||
"Project without `tool.ty` section: '{}'",
|
||||
closest_project.root()
|
||||
);
|
||||
|
||||
closest_project
|
||||
} else {
|
||||
tracing::debug!(
|
||||
"The ancestor directories contain no `pyproject.toml`. Falling back to a virtual project."
|
||||
);
|
||||
|
||||
// Create a project with a default configuration
|
||||
Self::new(
|
||||
path.file_name().unwrap_or("root").into(),
|
||||
path.to_path_buf(),
|
||||
)
|
||||
};
|
||||
|
||||
Ok(metadata)
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn root(&self) -> &SystemPath {
|
||||
|
|
@ -344,6 +376,36 @@ pub enum ProjectMetadataError {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DiscoveredProject {
|
||||
PyProject {
|
||||
has_ty_section: bool,
|
||||
metadata: ProjectMetadata,
|
||||
},
|
||||
Ty {
|
||||
metadata: ProjectMetadata,
|
||||
},
|
||||
}
|
||||
|
||||
impl DiscoveredProject {
|
||||
fn is_ty_or_project_with_ty_section(&self) -> bool {
|
||||
match self {
|
||||
DiscoveredProject::PyProject {
|
||||
has_ty_section: tool_ty,
|
||||
..
|
||||
} => *tool_ty,
|
||||
DiscoveredProject::Ty { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_metadata(self) -> ProjectMetadata {
|
||||
match self {
|
||||
DiscoveredProject::PyProject { metadata, .. } => metadata,
|
||||
DiscoveredProject::Ty { metadata } => metadata,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//! Integration tests for project discovery
|
||||
|
|
|
|||
|
|
@ -499,6 +499,9 @@ impl Session {
|
|||
continue;
|
||||
};
|
||||
|
||||
// TODO: Discover all projects within this workspace, starting from root.
|
||||
//
|
||||
|
||||
// For now, create one project database per workspace.
|
||||
// In the future, index the workspace directories to find all projects
|
||||
// and create a project database for each.
|
||||
|
|
@ -1175,8 +1178,7 @@ impl Workspaces {
|
|||
) -> Option<(SystemPathBuf, &mut Workspace)> {
|
||||
let path = url.to_file_path().ok()?;
|
||||
|
||||
// Realistically I don't think this can fail because we got the path from a Url
|
||||
let system_path = SystemPathBuf::from_path_buf(path).ok()?;
|
||||
let system_path = SystemPathBuf::from_path_buf(path).expect("URL to be valid UTF-8");
|
||||
|
||||
if let Some(workspace) = self.workspaces.get_mut(&system_path) {
|
||||
workspace.settings = Arc::new(settings);
|
||||
|
|
|
|||
Loading…
Reference in New Issue