diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 8ab505ff..9146ec52 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -6,6 +6,7 @@ import functools import logging import os import re +import shutil import subprocess import time import unicodedata @@ -247,7 +248,15 @@ def _compute_cols(hooks: Sequence[Hook]) -> int: name_len = 0 cols = name_len + 3 + len(NO_FILES) + 1 + len(SKIPPED) - return max(cols, 80) + cols = max(cols, 80) + + # Cap at terminal width to prevent wrapping + try: + term_width = shutil.get_terminal_size().columns + except OSError: + term_width = 80 + + return min(cols, term_width) def _all_filenames(args: argparse.Namespace) -> Iterable[str]: diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index e4af1e16..baf45ddf 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -574,21 +574,45 @@ def test_rebase(cap_out, store, repo_with_passing_hook): @pytest.mark.parametrize( - ('hooks', 'expected'), + ('term_width', 'hooks', 'expected'), ( - ([], 80), - ([auto_namedtuple(id='a', name='a' * 51)], 81), + # Default 80-column terminal + (80, [], 80), + (80, [auto_namedtuple(id='a', name='a' * 51)], 80), ( + 80, + [ + auto_namedtuple(id='a', name='a' * 51), + auto_namedtuple(id='b', name='b' * 52), + ], + 80, + ), + # Wide terminal - ideal width is used + (120, [], 80), + (120, [auto_namedtuple(id='a', name='a' * 51)], 81), + ( + 120, [ auto_namedtuple(id='a', name='a' * 51), auto_namedtuple(id='b', name='b' * 52), ], 82, ), + # Narrow terminal - capped at terminal width + (70, [], 70), + (70, [auto_namedtuple(id='a', name='a' * 10)], 70), ), ) -def test_compute_cols(hooks, expected): - assert _compute_cols(hooks) == expected +def test_compute_cols(term_width, hooks, expected): + class FakeTerminalSize: + def __init__(self): + self.columns = term_width + + with mock.patch( + 'shutil.get_terminal_size', + return_value=FakeTerminalSize(), + ): + assert _compute_cols(hooks) == expected @pytest.mark.parametrize(