Browse Source

feat: display branches in columnar format

xifOO 4 months ago
parent
commit
d9e4df1752
1 changed files with 85 additions and 85 deletions
  1. 85 85
      dulwich/cli.py

+ 85 - 85
dulwich/cli.py

@@ -36,6 +36,7 @@ import signal
 import subprocess
 import subprocess
 import sys
 import sys
 import tempfile
 import tempfile
+from collections.abc import Iterator
 from pathlib import Path
 from pathlib import Path
 from typing import BinaryIO, Callable, ClassVar, Optional, Union
 from typing import BinaryIO, Callable, ClassVar, Optional, Union
 
 
@@ -190,6 +191,74 @@ def launch_editor(template_content: bytes = b"") -> bytes:
         os.unlink(temp_file)
         os.unlink(temp_file)
 
 
 
 
+def write_columns(items: Union[Iterator[bytes], list[bytes]]) -> None:
+    """Display items in formatted columns based on terminal width.
+
+    Args:
+        items: List or iterator of bytes objects to display in columns
+
+    The function calculates the optimal number of columns to fit the terminal
+    width and displays the items in a formatted column layout with proper
+    padding and alignment.
+    """
+    try:
+        ter_width = os.get_terminal_size().columns
+    except OSError:
+        ter_width = 80
+
+    item_names = [item.decode() for item in items]
+
+    def columns(names, width, num_cols):
+        if num_cols <= 0:
+            return False, []
+
+        num_rows = (len(names) + num_cols - 1) // num_cols
+        col_widths = []
+
+        for col in range(num_cols):
+            max_width = 0
+            for row in range(num_rows):
+                idx = row + col * num_rows
+                if idx < len(names):
+                    max_width = max(max_width, len(names[idx]))
+            col_widths.append(max_width + 2)  # add padding
+
+        total_width = sum(col_widths)
+        if total_width <= width:
+            return True, col_widths
+        return False, []
+
+    best_cols = 1
+    best_widths = []
+
+    for num_cols in range(min(8, len(item_names)), 0, -1):
+        fits, widths = columns(item_names, ter_width, num_cols)
+        if fits:
+            best_cols = num_cols
+            best_widths = widths
+            break
+
+    if not best_widths:
+        best_cols = 1
+        best_widths = [max(len(name) for name in item_names) + 2]
+
+    num_rows = (len(item_names) + best_cols - 1) // best_cols
+
+    for row in range(num_rows):
+        lines = []
+        for col in range(best_cols):
+            idx = row + col * num_rows
+            if idx < len(item_names):
+                branch_name = item_names[idx]
+                if col < len(best_widths):
+                    lines.append(branch_name.ljust(best_widths[col]))
+                else:
+                    lines.append(branch_name)
+
+        if lines:
+            sys.stdout.write("".join(lines).rstrip() + "\n")
+
+
 class PagerBuffer:
 class PagerBuffer:
     """Binary buffer wrapper for Pager to mimic sys.stdout.buffer."""
     """Binary buffer wrapper for Pager to mimic sys.stdout.buffer."""
 
 
@@ -2081,15 +2150,21 @@ class cmd_branch(Command):
         )
         )
         args = parser.parse_args(args)
         args = parser.parse_args(args)
 
 
+        def print_branches(
+            branches: Union[Iterator[bytes], list[bytes]], use_columns=False
+        ) -> None:
+            if use_columns:
+                write_columns(branches)
+            else:
+                for branch in branches:
+                    sys.stdout.write(f"{branch.decode()}\n")
+
         if args.all:
         if args.all:
             try:
             try:
                 branches = porcelain.branch_list(".") + porcelain.branch_remotes_list(
                 branches = porcelain.branch_list(".") + porcelain.branch_remotes_list(
                     "."
                     "."
                 )
                 )
-
-                for branch in branches:
-                    sys.stdout.write(f"{branch.decode()}\n")
-
+                print_branches(branches, args.column)
                 return 0
                 return 0
 
 
             except porcelain.Error as e:
             except porcelain.Error as e:
@@ -2099,11 +2174,9 @@ class cmd_branch(Command):
         if args.merged:
         if args.merged:
             try:
             try:
                 branches_iter = porcelain.merged_branches(".")
                 branches_iter = porcelain.merged_branches(".")
-
-                for branch in branches_iter:
-                    sys.stdout.write(f"{branch.decode()}\n")
-
+                print_branches(branches_iter, args.column)
                 return 0
                 return 0
+
             except porcelain.Error as e:
             except porcelain.Error as e:
                 sys.stderr.write(f"{e}")
                 sys.stderr.write(f"{e}")
                 return 1
                 return 1
@@ -2111,11 +2184,9 @@ class cmd_branch(Command):
         if args.no_merged:
         if args.no_merged:
             try:
             try:
                 branches_iter = porcelain.no_merged_branches(".")
                 branches_iter = porcelain.no_merged_branches(".")
-
-                for branch in branches_iter:
-                    sys.stdout.write(f"{branch.decode()}\n")
-
+                print_branches(branches_iter, args.column)
                 return 0
                 return 0
+
             except porcelain.Error as e:
             except porcelain.Error as e:
                 sys.stderr.write(f"{e}")
                 sys.stderr.write(f"{e}")
                 return 1
                 return 1
@@ -2123,10 +2194,7 @@ class cmd_branch(Command):
         if args.contains:
         if args.contains:
             try:
             try:
                 branches_iter = porcelain.branches_containing(".", commit=args.contains)
                 branches_iter = porcelain.branches_containing(".", commit=args.contains)
-
-                for branch in branches_iter:
-                    sys.stdout.write(f"{branch.decode()}\n")
-
+                print_branches(branches_iter, args.column)
                 return 0
                 return 0
 
 
             except KeyError as e:
             except KeyError as e:
@@ -2140,77 +2208,9 @@ class cmd_branch(Command):
         if args.remotes:
         if args.remotes:
             try:
             try:
                 branches = porcelain.branch_remotes_list(".")
                 branches = porcelain.branch_remotes_list(".")
-
-                for branch in branches:
-                    sys.stdout.write(f"{branch.decode()}\n")
-
+                print_branches(branches, args.column)
                 return 0
                 return 0
-            except porcelain.Error as e:
-                sys.stderr.write(f"{e}")
-                return 1
-
-        if args.column:
-            try:
-                branches = porcelain.branch_list(".")
 
 
-                try:
-                    ter_width = os.get_terminal_size().columns
-                except OSError:
-                    ter_width = 80
-
-                branch_names = [branch.decode() for branch in branches]
-
-                def columns(names, width, num_cols):
-                    if num_cols <= 0:
-                        return False, []
-
-                    num_rows = (len(names) + num_cols - 1) // num_cols
-                    col_widths = []
-
-                    for col in range(num_cols):
-                        max_width = 0
-                        for row in range(num_rows):
-                            idx = row + col * num_rows
-                            if idx < len(names):
-                                max_width = max(max_width, len(names[idx]))
-                        col_widths.append(max_width + 2)  # add padding
-
-                    total_width = sum(col_widths)
-                    if total_width <= width:
-                        return True, col_widths
-                    return False, []
-
-                best_cols = 1
-                best_widths = []
-
-                for num_cols in range(min(8, len(branch_names)), 0, -1):
-                    fits, widths = columns(branch_names, ter_width, num_cols)
-                    if fits:
-                        best_cols = num_cols
-                        best_widths = widths
-                        break
-
-                if not best_widths:
-                    best_cols = 1
-                    best_widths = [max(len(name) for name in branch_names) + 2]
-
-                num_rows = (len(branch_names) + best_cols - 1) // best_cols
-
-                for row in range(num_rows):
-                    lines = []
-                    for col in range(best_cols):
-                        idx = row + col * num_rows
-                        if idx < len(branch_names):
-                            branch_name = branch_names[idx]
-                            if col < len(best_widths):
-                                lines.append(branch_name.ljust(best_widths[col]))
-                            else:
-                                lines.append(branch_name)
-
-                    if lines:
-                        sys.stdout.write("".join(lines).rstrip() + "\n")
-
-                return 0
             except porcelain.Error as e:
             except porcelain.Error as e:
                 sys.stderr.write(f"{e}")
                 sys.stderr.write(f"{e}")
                 return 1
                 return 1