diff --git a/crates/ruff_cli/src/args.rs b/crates/ruff_cli/src/args.rs index 58ba3302c6..478d779628 100644 --- a/crates/ruff_cli/src/args.rs +++ b/crates/ruff_cli/src/args.rs @@ -57,6 +57,13 @@ pub enum Command { /// Generate shell completion. #[clap(alias = "--generate-shell-completion", hide = true)] GenerateShellCompletion { shell: clap_complete_command::Shell }, + /// Format the given files, or stdin when using `-`. + #[doc(hidden)] + #[clap(hide = true)] + Format { + /// List of files or directories to format or `-` for stdin + files: Vec, + }, } #[derive(Debug, clap::Args)] diff --git a/crates/ruff_cli/src/commands/run_stdin.rs b/crates/ruff_cli/src/commands/run_stdin.rs index 817de79c57..f0ac60f32a 100644 --- a/crates/ruff_cli/src/commands/run_stdin.rs +++ b/crates/ruff_cli/src/commands/run_stdin.rs @@ -11,7 +11,7 @@ use crate::args::Overrides; use crate::diagnostics::{lint_stdin, Diagnostics}; /// Read a `String` from `stdin`. -fn read_from_stdin() -> Result { +pub(crate) fn read_from_stdin() -> Result { let mut buffer = String::new(); io::stdin().lock().read_to_string(&mut buffer)?; Ok(buffer) diff --git a/crates/ruff_cli/src/lib.rs b/crates/ruff_cli/src/lib.rs index 752f6efa25..1ff17a3c55 100644 --- a/crates/ruff_cli/src/lib.rs +++ b/crates/ruff_cli/src/lib.rs @@ -1,10 +1,11 @@ -use std::io::{self, BufWriter}; -use std::path::PathBuf; +use std::io::{self, stdout, BufWriter, Write}; +use std::path::{Path, PathBuf}; use std::process::ExitCode; use std::sync::mpsc::channel; -use anyhow::Result; +use anyhow::{Context, Result}; use clap::CommandFactory; +use log::warn; use notify::{recommended_watcher, RecursiveMode, Watcher}; use ruff::logging::{set_up_logging, LogLevel}; @@ -13,6 +14,7 @@ use ruff::settings::{flags, CliSettings}; use ruff::{fs, warn_user_once}; use crate::args::{Args, CheckArgs, Command}; +use crate::commands::run_stdin::read_from_stdin; use crate::printer::{Flags as PrinterFlags, Printer}; pub mod args; @@ -117,11 +119,41 @@ quoting the executed command, along with the relevant file contents and `pyproje shell.generate(&mut Args::command(), &mut io::stdout()); } Command::Check(args) => return check(args, log_level), + Command::Format { files } => return format(&files), } Ok(ExitStatus::Success) } +fn format(files: &[PathBuf]) -> Result { + warn_user_once!( + "`ruff format` is a work-in-progress, subject to change at any time, and intended for \ + internal use only." + ); + + // dummy + let format_code = |code: &str| code.replace("# DEL", ""); + + match &files { + // Check if we should read from stdin + [path] if path == Path::new("-") => { + let unformatted = read_from_stdin()?; + let formatted = format_code(&unformatted); + stdout().lock().write_all(formatted.as_bytes())?; + } + _ => { + for file in files { + let unformatted = std::fs::read_to_string(file) + .with_context(|| format!("Could not read {}: ", file.display()))?; + let formatted = format_code(&unformatted); + std::fs::write(file, formatted) + .with_context(|| format!("Could not write to {}, exiting", file.display()))?; + } + } + } + Ok(ExitStatus::Success) +} + fn check(args: CheckArgs, log_level: LogLevel) -> Result { #[cfg(feature = "ecosystem_ci")] let ecosystem_ci = args.ecosystem_ci;