From b158999c657197d45dca53a37127b561d18b1719 Mon Sep 17 00:00:00 2001 From: Jo <10510431+j178@users.noreply.github.com> Date: Sat, 18 Jul 2026 13:14:49 +0800 Subject: [PATCH] Reduce hook dispatch await points --- crates/prek/src/hooks/builtin_hooks/mod.rs | 71 +++++++++++-------- crates/prek/src/hooks/mod.rs | 4 ++ crates/prek/src/hooks/pre_commit_hooks/mod.rs | 47 ++++++------ 3 files changed, 70 insertions(+), 52 deletions(-) diff --git a/crates/prek/src/hooks/builtin_hooks/mod.rs b/crates/prek/src/hooks/builtin_hooks/mod.rs index 3f22af886..a2204700c 100644 --- a/crates/prek/src/hooks/builtin_hooks/mod.rs +++ b/crates/prek/src/hooks/builtin_hooks/mod.rs @@ -10,6 +10,8 @@ use crate::hook::Hook; use crate::hooks::pre_commit_hooks; use crate::store::Store; +use super::HookFuture; + mod check_illegal_windows_names; mod check_json5; mod pattern; @@ -96,53 +98,62 @@ impl BuiltinHooks { reporter: &HookRunReporter, ) -> Result<(i32, Vec)> { let progress = reporter.on_run_start(hook, filenames.len()); - let result = match self { + let future: HookFuture<'_> = match self { Self::CheckAddedLargeFiles => { - pre_commit_hooks::check_added_large_files(hook, filenames).await + Box::pin(pre_commit_hooks::check_added_large_files(hook, filenames)) } - Self::CheckCaseConflict => pre_commit_hooks::check_case_conflict(hook, filenames).await, - Self::CheckExecutablesHaveShebangs => { - pre_commit_hooks::check_executables_have_shebangs(hook, filenames).await + Self::CheckCaseConflict => { + Box::pin(pre_commit_hooks::check_case_conflict(hook, filenames)) } - Self::CheckIllegalWindowsNames => Ok( - check_illegal_windows_names::check_illegal_windows_names(hook, filenames), + Self::CheckExecutablesHaveShebangs => Box::pin( + pre_commit_hooks::check_executables_have_shebangs(hook, filenames), ), - Self::CheckJson => pre_commit_hooks::check_json(hook, filenames).await, - Self::CheckJson5 => check_json5::check_json5(hook, filenames).await, + Self::CheckIllegalWindowsNames => Box::pin(std::future::ready(Ok( + check_illegal_windows_names::check_illegal_windows_names(hook, filenames), + ))), + Self::CheckJson => Box::pin(pre_commit_hooks::check_json(hook, filenames)), + Self::CheckJson5 => Box::pin(check_json5::check_json5(hook, filenames)), Self::CheckMergeConflict => { - pre_commit_hooks::check_merge_conflict(hook, filenames).await - } - Self::CheckShebangScriptsAreExecutable => { - pre_commit_hooks::check_shebang_scripts_are_executable(hook, filenames).await + Box::pin(pre_commit_hooks::check_merge_conflict(hook, filenames)) } - Self::CheckSymlinks => pre_commit_hooks::check_symlinks(hook, filenames).await, - Self::CheckToml => pre_commit_hooks::check_toml(hook, filenames).await, + Self::CheckShebangScriptsAreExecutable => Box::pin( + pre_commit_hooks::check_shebang_scripts_are_executable(hook, filenames), + ), + Self::CheckSymlinks => Box::pin(pre_commit_hooks::check_symlinks(hook, filenames)), + Self::CheckToml => Box::pin(pre_commit_hooks::check_toml(hook, filenames)), Self::CheckVcsPermalinks => { - pre_commit_hooks::check_vcs_permalinks(hook, filenames).await + Box::pin(pre_commit_hooks::check_vcs_permalinks(hook, filenames)) + } + Self::CheckXml => Box::pin(pre_commit_hooks::check_xml(hook, filenames)), + Self::CheckYaml => Box::pin(pre_commit_hooks::check_yaml(hook, filenames)), + Self::DenyPattern => Box::pin(pattern::deny_pattern(hook, filenames)), + Self::DestroyedSymlinks => { + Box::pin(pre_commit_hooks::destroyed_symlinks(hook, filenames)) } - Self::CheckXml => pre_commit_hooks::check_xml(hook, filenames).await, - Self::CheckYaml => pre_commit_hooks::check_yaml(hook, filenames).await, - Self::DenyPattern => pattern::deny_pattern(hook, filenames).await, - Self::DestroyedSymlinks => pre_commit_hooks::destroyed_symlinks(hook, filenames).await, - Self::DetectPrivateKey => pre_commit_hooks::detect_private_key(hook, filenames).await, - Self::EndOfFileFixer => pre_commit_hooks::fix_end_of_file(hook, filenames).await, + Self::DetectPrivateKey => { + Box::pin(pre_commit_hooks::detect_private_key(hook, filenames)) + } + Self::EndOfFileFixer => Box::pin(pre_commit_hooks::fix_end_of_file(hook, filenames)), Self::FileContentsSorter => { - pre_commit_hooks::file_contents_sorter(hook, filenames).await + Box::pin(pre_commit_hooks::file_contents_sorter(hook, filenames)) } Self::FixByteOrderMarker => { - pre_commit_hooks::fix_byte_order_marker(hook, filenames).await + Box::pin(pre_commit_hooks::fix_byte_order_marker(hook, filenames)) } Self::ForbidNewSubmodules => { - pre_commit_hooks::forbid_new_submodules(hook, filenames).await + Box::pin(pre_commit_hooks::forbid_new_submodules(hook, filenames)) + } + Self::MixedLineEnding => Box::pin(pre_commit_hooks::mixed_line_ending(hook, filenames)), + Self::NoCommitToBranch => Box::pin(pre_commit_hooks::no_commit_to_branch(hook)), + Self::PrettyFormatJson => { + Box::pin(pre_commit_hooks::pretty_format_json(hook, filenames)) } - Self::MixedLineEnding => pre_commit_hooks::mixed_line_ending(hook, filenames).await, - Self::NoCommitToBranch => pre_commit_hooks::no_commit_to_branch(hook).await, - Self::PrettyFormatJson => pre_commit_hooks::pretty_format_json(hook, filenames).await, - Self::RequirePattern => pattern::require_pattern(hook, filenames).await, + Self::RequirePattern => Box::pin(pattern::require_pattern(hook, filenames)), Self::TrailingWhitespace => { - pre_commit_hooks::fix_trailing_whitespace(hook, filenames).await + Box::pin(pre_commit_hooks::fix_trailing_whitespace(hook, filenames)) } }; + let result = future.await; reporter.on_run_complete(progress); result } diff --git a/crates/prek/src/hooks/mod.rs b/crates/prek/src/hooks/mod.rs index 97c2eb1ee..7cf9b2d62 100644 --- a/crates/prek/src/hooks/mod.rs +++ b/crates/prek/src/hooks/mod.rs @@ -1,5 +1,6 @@ use std::future::Future; use std::path::Path; +use std::pin::Pin; use std::str::FromStr; use std::sync::LazyLock; @@ -16,6 +17,9 @@ mod builtin_hooks; mod meta_hooks; mod pre_commit_hooks; +// Erase hook implementation futures before awaiting them so dispatchers have one suspension point. +type HookFuture<'a> = Pin)>> + 'a>>; + static NO_FAST_PATH: LazyLock = LazyLock::new(|| EnvVars.is_set(EnvVars::PREK_NO_FAST_PATH)); /// Returns true if the hook has a builtin Rust implementation. diff --git a/crates/prek/src/hooks/pre_commit_hooks/mod.rs b/crates/prek/src/hooks/pre_commit_hooks/mod.rs index 3f5ff4b22..0d746fb69 100644 --- a/crates/prek/src/hooks/pre_commit_hooks/mod.rs +++ b/crates/prek/src/hooks/pre_commit_hooks/mod.rs @@ -5,6 +5,8 @@ use tracing::debug; use crate::hook::Hook; +use super::HookFuture; + mod check_added_large_files; mod check_case_conflict; mod check_executables_have_shebangs; @@ -117,32 +119,33 @@ impl PreCommitHooks { pub(crate) async fn run(self, hook: &Hook, filenames: &[&Path]) -> Result<(i32, Vec)> { debug!("Running hook `{}` in fast path", hook.id); - match self { - Self::CheckAddedLargeFiles => check_added_large_files(hook, filenames).await, - Self::CheckCaseConflict => check_case_conflict(hook, filenames).await, + let future: HookFuture<'_> = match self { + Self::CheckAddedLargeFiles => Box::pin(check_added_large_files(hook, filenames)), + Self::CheckCaseConflict => Box::pin(check_case_conflict(hook, filenames)), Self::CheckExecutablesHaveShebangs => { - check_executables_have_shebangs(hook, filenames).await + Box::pin(check_executables_have_shebangs(hook, filenames)) } Self::CheckShebangScriptsAreExecutable => { - check_shebang_scripts_are_executable(hook, filenames).await + Box::pin(check_shebang_scripts_are_executable(hook, filenames)) } - Self::CheckVcsPermalinks => check_vcs_permalinks(hook, filenames).await, - Self::FileContentsSorter => file_contents_sorter(hook, filenames).await, - Self::EndOfFileFixer => fix_end_of_file(hook, filenames).await, - Self::FixByteOrderMarker => fix_byte_order_marker(hook, filenames).await, - Self::ForbidNewSubmodules => forbid_new_submodules(hook, filenames).await, - Self::CheckJson => check_json(hook, filenames).await, - Self::CheckSymlinks => check_symlinks(hook, filenames).await, - Self::CheckMergeConflict => check_merge_conflict(hook, filenames).await, - Self::CheckToml => check_toml(hook, filenames).await, - Self::CheckYaml => check_yaml(hook, filenames).await, - Self::CheckXml => check_xml(hook, filenames).await, - Self::DestroyedSymlinks => destroyed_symlinks(hook, filenames).await, - Self::MixedLineEnding => mixed_line_ending(hook, filenames).await, - Self::DetectPrivateKey => detect_private_key(hook, filenames).await, - Self::NoCommitToBranch => no_commit_to_branch(hook).await, - Self::TrailingWhitespace => fix_trailing_whitespace(hook, filenames).await, - } + Self::CheckVcsPermalinks => Box::pin(check_vcs_permalinks(hook, filenames)), + Self::FileContentsSorter => Box::pin(file_contents_sorter(hook, filenames)), + Self::EndOfFileFixer => Box::pin(fix_end_of_file(hook, filenames)), + Self::FixByteOrderMarker => Box::pin(fix_byte_order_marker(hook, filenames)), + Self::ForbidNewSubmodules => Box::pin(forbid_new_submodules(hook, filenames)), + Self::CheckJson => Box::pin(check_json(hook, filenames)), + Self::CheckSymlinks => Box::pin(check_symlinks(hook, filenames)), + Self::CheckMergeConflict => Box::pin(check_merge_conflict(hook, filenames)), + Self::CheckToml => Box::pin(check_toml(hook, filenames)), + Self::CheckYaml => Box::pin(check_yaml(hook, filenames)), + Self::CheckXml => Box::pin(check_xml(hook, filenames)), + Self::DestroyedSymlinks => Box::pin(destroyed_symlinks(hook, filenames)), + Self::MixedLineEnding => Box::pin(mixed_line_ending(hook, filenames)), + Self::DetectPrivateKey => Box::pin(detect_private_key(hook, filenames)), + Self::NoCommitToBranch => Box::pin(no_commit_to_branch(hook)), + Self::TrailingWhitespace => Box::pin(fix_trailing_whitespace(hook, filenames)), + }; + future.await } }