From 7915bc61345b446ae959c2cb8dc6d281ab81c21f Mon Sep 17 00:00:00 2001 From: pullfrog Date: Fri, 19 Dec 2025 15:16:39 +0000 Subject: [PATCH] Add exponential function spacing option This commit adds a new --exponential-function-spacing flag that applies exponentially increasing blank lines between function definitions. The spacing follows the pattern 2^n where n is the function index (0-based): - 1st to 2nd function: 1 blank line (2^0) - 2nd to 3rd function: 2 blank lines (2^1) - 3rd to 4th function: 4 blank lines (2^2) - 4th to 5th function: 8 blank lines (2^3) This feature discourages developers from creating too many functions in a single file by making the visual separation increasingly dramatic. Changes: - Added exponential_function_spacing boolean field to Mode class - Updated Mode.__hash__ and get_cache_key to include new field - Added function_count_at_depth tracking to EmptyLineTracker - Modified empty line calculation logic to apply exponential spacing - Added --exponential-function-spacing CLI option --- src/black/__init__.py | 11 +++++++++++ src/black/lines.py | 10 +++++++++- src/black/mode.py | 3 +++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/black/__init__.py b/src/black/__init__.py index 180f5883b1d..2e8206eee4b 100644 --- a/src/black/__init__.py +++ b/src/black/__init__.py @@ -331,6 +331,15 @@ def validate_regex( " or existence of any unstable features." ), ) +@click.option( + "--exponential-function-spacing", + "exponential_function_spacing", + is_flag=True, + help=( + "Add exponentially increasing empty lines between function definitions to" + " discourage creating too many functions in a file." + ), +) @click.option( "--check", is_flag=True, @@ -532,6 +541,7 @@ def main( preview: bool, unstable: bool, enable_unstable_feature: list[Preview], + exponential_function_spacing: bool, quiet: bool, verbose: bool, required_version: str | None, @@ -640,6 +650,7 @@ def main( unstable=unstable, python_cell_magics=set(python_cell_magics), enabled_features=set(enable_unstable_feature), + exponential_function_spacing=exponential_function_spacing, ) lines: list[tuple[int, int]] = [] diff --git a/src/black/lines.py b/src/black/lines.py index 09fce3a193d..acdc6c8bf67 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -544,6 +544,7 @@ class EmptyLineTracker: previous_block: LinesBlock | None = None previous_defs: list[Line] = field(default_factory=list) semantic_leading_comment: LinesBlock | None = None + function_count_at_depth: dict[int, int] = field(default_factory=dict) def maybe_empty_lines(self, current_line: Line) -> LinesBlock: """Return the number of extra empty lines before and after the `current_line`. @@ -684,6 +685,10 @@ def _maybe_empty_lines(self, current_line: Line) -> tuple[int, int]: before = 1 else: before = 2 + if self.mode.exponential_function_spacing and current_line.is_def: + function_index = self.function_count_at_depth.get(depth, 0) + self.function_count_at_depth[depth] = function_index + 1 + before = 2 ** function_index if current_line.is_decorator or current_line.is_def or current_line.is_class: return self._maybe_empty_lines_for_class_or_def( @@ -776,7 +781,10 @@ def _maybe_empty_lines_for_class_or_def( else: newlines = 0 else: - newlines = 1 if current_line.depth else 2 + if self.mode.exponential_function_spacing and current_line.is_def: + newlines = before + else: + newlines = 1 if current_line.depth else 2 # If a user has left no space after a dummy implementation, don't insert # new lines. This is useful for instance for @overload or Protocols. if self.previous_line.is_stub_def and not user_had_newline: diff --git a/src/black/mode.py b/src/black/mode.py index 702f580e979..c467dd83b74 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -269,6 +269,7 @@ class Mode: preview: bool = False unstable: bool = False enabled_features: set[Preview] = field(default_factory=set) + exponential_function_spacing: bool = False def __contains__(self, feature: Preview) -> bool: """ @@ -316,6 +317,7 @@ def get_cache_key(self) -> str: str(int(self.preview)), str(int(self.unstable)), features_and_magics, + str(int(self.exponential_function_spacing)), ] return ".".join(parts) @@ -332,4 +334,5 @@ def __hash__(self) -> int: self.preview, self.unstable, frozenset(self.enabled_features), + self.exponential_function_spacing, ))