Skip to content

Commit

Permalink
Feat: detect changes also in configuration files (#4169)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeleppane authored May 9, 2023
1 parent 318653c commit 04e8e74
Showing 1 changed file with 138 additions and 17 deletions.
155 changes: 138 additions & 17 deletions crates/ruff_cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,36 @@ impl From<ExitStatus> for ExitCode {
}
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum ChangeKind {
Configuration,
SourceFile,
}

/// Return the [`ChangeKind`] based on the list of modified file paths.
///
/// Returns `None` if no relevant changes were detected.
fn change_detected(paths: &[PathBuf]) -> Option<ChangeKind> {
// If any `.toml` files were modified, return `ChangeKind::Configuration`. Otherwise, return
// `ChangeKind::SourceFile` if any `.py`, `.pyi`, or `.pyw` files were modified.
let mut source_file = false;
for path in paths {
if let Some(suffix) = path.extension() {
match suffix.to_str() {
Some("toml") => {
return Some(ChangeKind::Configuration);
}
Some("py" | "pyi" | "pyw") => source_file = true,
_ => {}
}
}
}
if source_file {
return Some(ChangeKind::SourceFile);
}
None
}

pub fn run(
Args {
command,
Expand Down Expand Up @@ -207,30 +237,41 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
for file in &cli.files {
watcher.watch(file, RecursiveMode::Recursive)?;
}
if let Some(file) = pyproject_config.path.as_ref() {
watcher.watch(file, RecursiveMode::Recursive)?;
}

// In watch mode, we may need to re-resolve the configuration.
// TODO(charlie): Re-compute other derivative values, like the `printer`.
let mut pyproject_config = pyproject_config;

loop {
match rx.recv() {
Ok(event) => {
let paths = event?.paths;
let py_changed = paths.iter().any(|path| {
path.extension()
.map(|ext| ext == "py" || ext == "pyi")
.unwrap_or_default()
});
if py_changed {
Printer::clear_screen()?;
printer.write_to_user("File change detected...\n");

let messages = commands::run::run(
&cli.files,
&pyproject_config,
let Some(change_kind) = change_detected(&event?.paths) else {
continue;
};

if matches!(change_kind, ChangeKind::Configuration) {
pyproject_config = resolve::resolve(
cli.isolated,
cli.config.as_deref(),
&overrides,
cache.into(),
noqa.into(),
autofix,
cli.stdin_filename.as_deref(),
)?;
printer.write_continuously(&messages)?;
}
Printer::clear_screen()?;
printer.write_to_user("File change detected...\n");

let messages = commands::run::run(
&cli.files,
&pyproject_config,
&overrides,
cache.into(),
noqa.into(),
autofix,
)?;
printer.write_continuously(&messages)?;
}
Err(err) => return Err(err.into()),
}
Expand Down Expand Up @@ -311,3 +352,83 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
}
Ok(ExitStatus::Success)
}

#[cfg(test)]
mod test_file_change_detector {
use crate::{change_detected, ChangeKind};
use std::path::PathBuf;

#[test]
fn detect_correct_file_change() {
assert_eq!(
Some(ChangeKind::Configuration),
change_detected(&[
PathBuf::from("tmp/pyproject.toml"),
PathBuf::from("tmp/bin/ruff.rs"),
]),
);
assert_eq!(
Some(ChangeKind::Configuration),
change_detected(&[
PathBuf::from("pyproject.toml"),
PathBuf::from("tmp/bin/ruff.rs"),
]),
);
assert_eq!(
Some(ChangeKind::Configuration),
change_detected(&[
PathBuf::from("tmp1/tmp2/tmp3/pyproject.toml"),
PathBuf::from("tmp/bin/ruff.rs"),
]),
);
assert_eq!(
Some(ChangeKind::Configuration),
change_detected(&[
PathBuf::from("tmp/ruff.toml"),
PathBuf::from("tmp/bin/ruff.rs"),
]),
);
assert_eq!(
Some(ChangeKind::Configuration),
change_detected(&[
PathBuf::from("tmp/.ruff.toml"),
PathBuf::from("tmp/bin/ruff.rs"),
]),
);
assert_eq!(
Some(ChangeKind::SourceFile),
change_detected(&[
PathBuf::from("tmp/rule.py"),
PathBuf::from("tmp/bin/ruff.rs"),
]),
);
assert_eq!(
Some(ChangeKind::SourceFile),
change_detected(&[
PathBuf::from("tmp/rule.pyi"),
PathBuf::from("tmp/bin/ruff.rs"),
]),
);
assert_eq!(
Some(ChangeKind::Configuration),
change_detected(&[
PathBuf::from("pyproject.toml"),
PathBuf::from("tmp/rule.py"),
]),
);
assert_eq!(
Some(ChangeKind::Configuration),
change_detected(&[
PathBuf::from("tmp/rule.py"),
PathBuf::from("pyproject.toml"),
]),
);
assert_eq!(
None,
change_detected(&[
PathBuf::from("tmp/rule.js"),
PathBuf::from("tmp/bin/ruff.rs"),
]),
);
}
}

0 comments on commit 04e8e74

Please sign in to comment.