Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions crates/cli-lib/src/commands_fix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,28 @@ use crate::commands::FixArgs;
use crate::commands::Format;
use crate::linter;
use sqruff_lib::core::config::FluffConfig;
use sqruff_lib_core::dialects::init::DialectKind;
use std::path::Path;

pub(crate) fn run_fix(
args: FixArgs,
config: FluffConfig,
ignorer: impl Fn(&Path) -> bool + Send + Sync,
collect_parse_errors: bool,
dialect_override: Option<DialectKind>,
) -> i32 {
let FixArgs { paths, format } = args;
let mut linter = linter(config, format, collect_parse_errors);
let result = match linter.lint_paths(paths, true, &ignorer) {
let mut linter = linter(config.clone(), format, collect_parse_errors);

let result = crate::commands_lint::lint_paths_with_per_file_config(
&mut linter,
paths,
true,
&ignorer,
&config,
dialect_override,
);
let result = match result {
Ok(result) => result,
Err(e) => {
eprintln!("{}", e.value);
Expand Down Expand Up @@ -95,7 +106,7 @@ mod tests {
format: Format::Human,
};
let config = FluffConfig::default();
run_fix(args, config, ignore_none, true);
run_fix(args, config, ignore_none, true, None);

let after = std::fs::metadata(&path).unwrap().modified().unwrap();
assert_eq!(before, after);
Expand Down
110 changes: 106 additions & 4 deletions crates/cli-lib/src/commands_lint.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,50 @@
use crate::commands::{Format, LintArgs};
use crate::linter;
use sqruff_lib::core::config::FluffConfig;
use std::path::Path;
use sqruff_lib::core::config::{ConfigLoader, FluffConfig};
use sqruff_lib::core::linter::linting_result::LintingResult;
use sqruff_lib_core::dialects::init::DialectKind;
use std::collections::HashMap;
use std::path::{Path, PathBuf};

/// Build a FluffConfig for files whose nearest config directory is `config_dir`.
/// Falls back to `base_config` when `config_dir` is None.
/// Applies `dialect_override` on top if provided.
fn config_for_group(
config_dir: Option<&Path>,
base_config: &FluffConfig,
dialect_override: Option<DialectKind>,
) -> FluffConfig {
let mut config = match config_dir {
Some(dir) => FluffConfig::from_path(dir, None, false, None)
.unwrap_or_else(|_| base_config.clone()),
None => base_config.clone(),
};
if let Some(dialect) = dialect_override {
// Unwrap is safe: dialect was already validated in the CLI.
config.override_dialect(dialect).unwrap();
}
config
}

pub(crate) fn run_lint(
args: LintArgs,
config: FluffConfig,
ignorer: impl Fn(&Path) -> bool + Send + Sync,
collect_parse_errors: bool,
dialect_override: Option<DialectKind>,
) -> i32 {
let LintArgs { paths, format } = args;
let mut linter = linter(config, format, collect_parse_errors);
let result = match linter.lint_paths(paths, false, &ignorer) {
let mut linter = linter(config.clone(), format, collect_parse_errors);

let result = lint_paths_with_per_file_config(
&mut linter,
paths,
false,
&ignorer,
&config,
dialect_override,
);
let result = match result {
Ok(result) => result,
Err(e) => {
eprintln!("{}", e.value);
Expand Down Expand Up @@ -44,3 +77,72 @@ pub(crate) fn run_lint_stdin(

result.has_violations() as i32
}

/// Expand paths, group files by nearest config directory, and lint each group
/// with the appropriate config. When `dialect_override` is None (e.g. because
/// --config was given explicitly), per-file config resolution is skipped.
pub(crate) fn lint_paths_with_per_file_config(
linter: &mut sqruff_lib::core::linter::core::Linter,
paths: Vec<PathBuf>,
fix: bool,
ignorer: &(dyn Fn(&Path) -> bool + Send + Sync),
base_config: &FluffConfig,
dialect_override: Option<DialectKind>,
) -> Result<LintingResult, sqruff_lib_core::errors::SQLFluffUserError> {
// Expand directories to individual files.
let mut expanded: Vec<PathBuf> = Vec::new();
let input_paths = if paths.is_empty() {
vec![std::env::current_dir().unwrap()]
} else {
paths
};
for path in input_paths {
if path.is_file() {
expanded.push(path);
} else {
for p in linter.paths_from_path(path, None, None, None, None, Some(ignorer)) {
expanded.push(PathBuf::from(p));
}
}
}

let expanded: Vec<PathBuf> = expanded
.into_iter()
.filter(|path| {
let should_ignore = ignorer(path);
if should_ignore {
log::debug!(
"Filtering out ignored file '{}' from final processing list",
path.display()
);
}
!should_ignore
})
.collect();

if expanded.is_empty() {
return Ok(LintingResult::new(Vec::new()));
}

if dialect_override.is_some() {
// Group files by nearest config directory.
let mut groups: HashMap<Option<PathBuf>, Vec<PathBuf>> = HashMap::new();
for path in expanded {
let config_dir = ConfigLoader::find_nearest_config_dir(&path);
groups.entry(config_dir).or_default().push(path);
}

let mut all_files = Vec::new();
for (config_dir, group_paths) in groups {
let config =
config_for_group(config_dir.as_deref(), base_config, dialect_override);
linter.set_config(config);
let result = linter.lint_paths(group_paths, fix, ignorer)?;
all_files.extend(result);
}
Ok(LintingResult::new(all_files))
} else {
// No per-file resolution (explicit --config or no --dialect).
linter.lint_paths(expanded, fix, ignorer)
}
}
2 changes: 1 addition & 1 deletion crates/cli-lib/src/commands_parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ fn parse_and_output_tree(
if let Some(tree) = &parsed.tree {
match format {
ParseFormat::Json => {
let serialized = tree.to_serialised(false, true);
let serialized = tree.to_serialised(false, true, None);
match serde_json::to_string_pretty(&serialized) {
Ok(json) => println!("{}", json),
Err(e) => {
Expand Down
52 changes: 36 additions & 16 deletions crates/cli-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ where
let cli = Cli::parse_from(args);
let collect_parse_errors = cli.parsing_errors;

let dialect_override: Option<DialectKind> = cli.dialect.map(|dialect| {
DialectKind::try_from(dialect.as_str()).unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1);
})
});

let explicit_config = cli.config.is_some();
let mut config: FluffConfig = if let Some(config) = cli.config.as_ref() {
if !Path::new(config).is_file() {
eprintln!(
Expand All @@ -60,20 +68,11 @@ where
FluffConfig::from_root(None, false, None).unwrap()
};

if let Some(dialect) = cli.dialect {
let dialect_kind = DialectKind::try_from(dialect.as_str());
match dialect_kind {
Ok(dialect_kind) => {
config.override_dialect(dialect_kind).unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1);
});
}
Err(e) => {
eprintln!("{}", e);
std::process::exit(1);
}
}
if let Some(dialect) = dialect_override {
config.override_dialect(dialect).unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1);
});
}

let current_path = std::env::current_dir().unwrap();
Expand All @@ -84,21 +83,42 @@ where
move |path: &Path| ignore_file.is_ignored(path)
};

// Per-file config resolution is only used when no explicit --config was
// given. When it is used, dialect_override is re-applied on top of each
// per-file config.
let per_file_dialect = if explicit_config {
None
} else {
dialect_override
};

match cli.command {
Commands::Lint(args) => match is_std_in_flag_input(&args.paths) {
Err(e) => {
eprintln!("{e}");
1
}
Ok(false) => commands_lint::run_lint(args, config, ignorer, collect_parse_errors),
Ok(false) => commands_lint::run_lint(
args,
config,
ignorer,
collect_parse_errors,
per_file_dialect,
),
Ok(true) => commands_lint::run_lint_stdin(config, args.format, collect_parse_errors),
},
Commands::Fix(args) => match is_std_in_flag_input(&args.paths) {
Err(e) => {
eprintln!("{e}");
1
}
Ok(false) => commands_fix::run_fix(args, config, ignorer, collect_parse_errors),
Ok(false) => commands_fix::run_fix(
args,
config,
ignorer,
collect_parse_errors,
per_file_dialect,
),
Ok(true) => commands_fix::run_fix_stdin(config, args.format, collect_parse_errors),
},
Commands::Lsp => {
Expand Down
Loading
Loading