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, ))