Skip to content
Merged
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
15 changes: 11 additions & 4 deletions crates/prek/src/cli/list_builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,35 @@ pub(crate) fn list_builtins(
) -> anyhow::Result<ExitStatus> {
let hooks = BuiltinHooks::iter().map(|variant| {
let id = variant.as_ref();
BuiltinHook::from_id(id).expect("All BuiltinHooks variants should be valid")
let hook = BuiltinHook::from_id(id).expect("All BuiltinHooks variants should be valid");
(variant, hook)
});

match output_format {
ListOutputFormat::Text => {
if verbose {
for hook in hooks {
for (variant, hook) in hooks {
writeln!(printer.stdout_important(), "{}", hook.id.bold())?;
if let Some(description) = &hook.options.description {
writeln!(printer.stdout_important(), " {description}")?;
}
if let Some(flags_help) = variant.flags_help() {
writeln!(printer.stdout_important(), " flags:")?;
for line in flags_help.lines() {
writeln!(printer.stdout_important(), " {line}")?;
}
}
writeln!(printer.stdout_important())?;
}
} else {
for hook in hooks {
for (_, hook) in hooks {
writeln!(printer.stdout_important(), "{}", hook.id)?;
}
}
}
ListOutputFormat::Json => {
let serializable: Vec<_> = hooks
.map(|h| SerializableBuiltinHook {
.map(|(_, h)| SerializableBuiltinHook {
id: h.id,
name: h.name,
description: h.options.description,
Expand Down
77 changes: 51 additions & 26 deletions crates/prek/src/hooks/builtin_hooks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::path::Path;
use std::str::FromStr;

use anyhow::Result;
use clap::{Command, CommandFactory};
use prek_identify::tags;

use crate::cli::run::HookRunReporter;
Expand Down Expand Up @@ -60,6 +61,30 @@ pub(crate) enum BuiltinHooks {
}

impl BuiltinHooks {
fn flags_command(self) -> Option<Command> {
Some(match self {
Self::CheckAddedLargeFiles => {
pre_commit_hooks::check_added_large_files::Args::command()
}
Self::CheckMergeConflict => pre_commit_hooks::check_merge_conflict::Args::command(),
Self::CheckVcsPermalinks => pre_commit_hooks::check_vcs_permalinks::Args::command(),
Self::CheckYaml => pre_commit_hooks::check_yaml::Args::command(),
Self::DenyPattern | Self::RequirePattern => pattern::Args::command(),
Self::FileContentsSorter => pre_commit_hooks::file_contents_sorter::Args::command(),
Self::MixedLineEnding => pre_commit_hooks::mixed_line_ending::Args::command(),
Self::NoCommitToBranch => pre_commit_hooks::no_commit_to_branch::Args::command(),
Self::PrettyFormatJson => pre_commit_hooks::pretty_format_json::Args::command(),
Self::TrailingWhitespace => pre_commit_hooks::fix_trailing_whitespace::Args::command(),
_ => return None,
})
}

pub(crate) fn flags_help(self) -> Option<String> {
let mut command = self.flags_command()?.help_template("{options}");
let help = command.render_help().to_string();
if help.is_empty() { None } else { Some(help) }
}

pub(crate) fn may_modify_files(self) -> bool {
match self {
Self::EndOfFileFixer
Expand Down Expand Up @@ -175,7 +200,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("prevents giant files from being committed.".to_string()),
description: Some("Prevents giant files from being committed.".to_string()),
stages: Some([Stage::PreCommit, Stage::PrePush, Stage::Manual].into()),
..Default::default()
},
Expand All @@ -188,7 +213,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"checks for files that would conflict in case-insensitive filesystems"
"Checks for files that would conflict in case-insensitive filesystems."
.to_string(),
),
..Default::default()
Expand All @@ -202,7 +227,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"ensures that (non-binary) executables have a shebang.".to_string(),
"Ensures that (non-binary) executables have a shebang.".to_string(),
),
types: Some(tags::TAG_SET_EXECUTABLE_TEXT),
stages: Some([Stage::PreCommit, Stage::PrePush, Stage::Manual].into()),
Expand All @@ -217,7 +242,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"checks for filenames which cannot be created on Windows.".to_string(),
"Checks for filenames which cannot be created on Windows.".to_string(),
),
files: Some(
FilePattern::regex(
Expand All @@ -235,7 +260,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("checks json files for parseable syntax.".to_string()),
description: Some("Checks JSON files for parseable syntax.".to_string()),
types: Some(tags::TAG_SET_JSON),
..Default::default()
},
Expand All @@ -247,7 +272,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("checks json5 files for parseable syntax.".to_string()),
description: Some("Checks JSON5 files for parseable syntax.".to_string()),
types: Some(tags::TAG_SET_JSON5),
..Default::default()
},
Expand All @@ -260,7 +285,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"checks for files that contain merge conflict strings.".to_string(),
"Checks for files that contain merge conflict strings.".to_string(),
),
types: Some(tags::TAG_SET_TEXT),
..Default::default()
Expand All @@ -274,7 +299,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"ensures that (non-binary) files with a shebang are executable."
"Ensures that (non-binary) files with a shebang are executable."
.to_string(),
),
types: Some(tags::TAG_SET_TEXT),
Expand All @@ -290,7 +315,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"checks for symlinks which do not point to anything.".to_string(),
"Checks for symlinks which do not point to anything.".to_string(),
),
types: Some(tags::TAG_SET_SYMLINK),
..Default::default()
Expand All @@ -303,7 +328,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("checks toml files for parseable syntax.".to_string()),
description: Some("Checks TOML files for parseable syntax.".to_string()),
types: Some(tags::TAG_SET_TOML),
..Default::default()
},
Expand All @@ -316,7 +341,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"ensures that links to vcs websites are permalinks.".to_string(),
"Ensures that links to VCS websites are permalinks.".to_string(),
),
types: Some(tags::TAG_SET_TEXT),
..Default::default()
Expand All @@ -329,7 +354,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("checks xml files for parseable syntax.".to_string()),
description: Some("Checks XML files for parseable syntax.".to_string()),
types: Some(tags::TAG_SET_XML),
..Default::default()
},
Expand All @@ -341,7 +366,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("checks yaml files for parseable syntax.".to_string()),
description: Some("Checks YAML files for parseable syntax.".to_string()),
types: Some(tags::TAG_SET_YAML),
..Default::default()
},
Expand All @@ -354,7 +379,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"fails if any file contains a matching regular expression.".to_string(),
"Fails if any file contains a matching regular expression.".to_string(),
),
types: Some(tags::TAG_SET_TEXT),
..Default::default()
Expand All @@ -368,7 +393,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"detects symlinks that were replaced with regular files whose contents are the original symlink target path.".to_string(),
"Detects symlinks that were replaced with regular files whose contents are the original symlink target path.".to_string(),
),
types: Some(tags::TAG_SET_FILE),
stages: Some([Stage::PreCommit, Stage::PrePush, Stage::Manual].into()),
Expand All @@ -382,7 +407,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("detects the presence of private keys.".to_string()),
description: Some("Detects the presence of private keys.".to_string()),
types: Some(tags::TAG_SET_TEXT),
..Default::default()
},
Expand All @@ -395,7 +420,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"ensures that a file is either empty, or ends with one newline."
"Ensures that a file is either empty, or ends with one newline."
.to_string(),
),
types: Some(tags::TAG_SET_TEXT),
Expand All @@ -411,7 +436,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"sorts the lines in specified files (defaults to alphabetical)."
"Sorts the lines in specified files (defaults to alphabetical)."
.to_string(),
),
files: Some(FilePattern::Never),
Expand All @@ -425,7 +450,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("removes utf-8 byte order marker.".to_string()),
description: Some("Removes UTF-8 byte order marker.".to_string()),
types: Some(tags::TAG_SET_TEXT),
..Default::default()
},
Expand All @@ -437,7 +462,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("Prevent addition of new git submodules.".to_string()),
description: Some("Prevents the addition of new Git submodules.".to_string()),
types: Some(tags::TAG_SET_DIRECTORY),
..Default::default()
},
Expand All @@ -449,7 +474,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("replaces or checks mixed line ending.".to_string()),
description: Some("Replaces or checks mixed line endings.".to_string()),
types: Some(tags::TAG_SET_TEXT),
..Default::default()
},
Expand All @@ -462,7 +487,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"protects specific branches from direct commits.".to_string(),
"Protects specific branches from direct commits.".to_string(),
),
pass_filenames: Some(PassFilenames::None),
always_run: Some(true),
Expand All @@ -476,7 +501,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("checks that JSON files are pretty-formatted.".to_string()),
description: Some("Checks that JSON files are pretty-formatted.".to_string()),
types: Some(tags::TAG_SET_JSON),
stages: Some([Stage::PreCommit, Stage::PrePush, Stage::Manual].into()),
..Default::default()
Expand All @@ -490,7 +515,7 @@ impl BuiltinHook {
groups: None,
options: HookOptions {
description: Some(
"fails if any file does not contain a matching regular expression."
"Fails if any file does not contain a matching regular expression."
.to_string(),
),
types: Some(tags::TAG_SET_TEXT),
Expand All @@ -504,7 +529,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("sorts entries in requirements.txt.".to_string()),
description: Some("Sorts entries in requirements.txt.".to_string()),
files: Some(
FilePattern::regex(r"(requirements|constraints).*\.txt$")
.expect("builtin files regex must be valid"),
Expand All @@ -519,7 +544,7 @@ impl BuiltinHook {
priority: None,
groups: None,
options: HookOptions {
description: Some("trims trailing whitespace.".to_string()),
description: Some("Trims trailing whitespace.".to_string()),
types: Some(tags::TAG_SET_TEXT),
stages: Some([Stage::PreCommit, Stage::PrePush, Stage::Manual].into()),
..Default::default()
Expand Down
4 changes: 3 additions & 1 deletion crates/prek/src/hooks/builtin_hooks/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ use crate::run::INTERNAL_CONCURRENCY;
#[command(disable_help_subcommand = true)]
#[command(disable_version_flag = true)]
#[command(disable_help_flag = true)]
struct Args {
pub(crate) struct Args {
/// Match patterns case-insensitively.
#[arg(short = 'i', long)]
ignore_case: bool,
/// Search each file as a whole.
#[arg(short = 'm', long)]
multiline: bool,
#[arg(required = true, value_name = "PATTERN")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ use crate::run::INTERNAL_CONCURRENCY;
#[command(disable_help_subcommand = true)]
#[command(disable_version_flag = true)]
#[command(disable_help_flag = true)]
struct Args {
pub(crate) struct Args {
/// Check all files, not just those staged for addition.
#[arg(long)]
enforce_all: bool,
/// Maximum allowed file size in KiB.
#[arg(long = "maxkb", default_value = "500")]
max_kb: u64,
#[arg(value_name = "FILENAMES")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const SEPARATOR_PATTERNS: &[&[u8]] = &[b"======= ", b"=======\r\n", b"=======\n"
#[command(disable_help_subcommand = true)]
#[command(disable_version_flag = true)]
#[command(disable_help_flag = true)]
struct Args {
pub(crate) struct Args {
/// Run even when no merge or rebase is detected.
#[arg(long)]
assume_in_merge: bool,
#[arg(value_name = "FILENAMES")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ use crate::run::INTERNAL_CONCURRENCY;
#[command(disable_help_subcommand = true)]
#[command(disable_version_flag = true)]
#[command(disable_help_flag = true)]
struct Args {
#[arg(long = "additional-github-domain")]
pub(crate) struct Args {
/// Additional GitHub-style domain to check (repeatable).
#[arg(long = "additional-github-domain", value_name = "DOMAIN")]
additional_github_domains: Vec<String>,
#[arg(value_name = "FILENAMES")]
filenames: Vec<PathBuf>,
Expand Down
5 changes: 3 additions & 2 deletions crates/prek/src/hooks/pre_commit_hooks/check_yaml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ use crate::run::INTERNAL_CONCURRENCY;
#[command(disable_help_subcommand = true)]
#[command(disable_version_flag = true)]
#[command(disable_help_flag = true)]
struct Args {
#[arg(long, short = 'm', alias = "multi")]
pub(crate) struct Args {
/// Allow multiple YAML documents.
#[arg(long, short = 'm', visible_alias = "multi")]
allow_multiple_documents: bool,
#[arg(value_name = "FILENAMES")]
filenames: Vec<PathBuf>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ use crate::run::INTERNAL_CONCURRENCY;
#[command(disable_help_subcommand = true)]
#[command(disable_version_flag = true)]
#[command(disable_help_flag = true)]
struct Args {
pub(crate) struct Args {
/// Sort lines case-insensitively.
#[arg(long, conflicts_with = "unique")]
ignore_case: bool,
/// Remove duplicate lines.
#[arg(long, conflicts_with = "ignore_case")]
unique: bool,
#[arg(value_name = "FILENAMES")]
Expand Down
Loading
Loading