diff --git a/checkpkg.py b/checkpkg.py index 3488111..064c3b1 100755 --- a/checkpkg.py +++ b/checkpkg.py @@ -94,147 +94,193 @@ def fetch_package_data(package): return None def print_table(output, headers, selected_pms, args): - """Print formatted table with consistent alignment""" + """Print formatted table with consistent alignment or list for small terminals""" # Terminal dimensions terminal_width = shutil.get_terminal_size().columns - min_col_width = 10 - padding = 1 # Space on each side of the column content - # Calculate initial column widths based on content - pkg_col_width = max( - len(headers[0]), - max(len(pkg) for pkg in args.packages) if args.packages else min_col_width - ) + (padding * 2) + # Calculate package availability for coloring + pkg_availability = {} + for pkg in args.packages: + versions = output.get(pkg, {}) + available_count = sum(1 for pm in selected_pms if versions.get(pm, 'Not found') != 'Not found') + + if available_count == 0: + pkg_availability[pkg] = 'red' # Not available anywhere + elif available_count == len(selected_pms): + pkg_availability[pkg] = 'green' # Available everywhere + else: + pkg_availability[pkg] = 'yellow' # Available in some places - # Allocate remaining space proportionally to other columns - remaining_width = terminal_width - pkg_col_width - (3 * len(selected_pms)) # 3 chars for " | " - version_col_width = max(min_col_width, remaining_width // len(selected_pms)) + # Determine minimum required widths + min_pkg_width = max(len(pkg) for pkg in args.packages) + 2 + min_pm_width = 10 + min_required_width = min_pkg_width + (min_pm_width * len(selected_pms)) + (3 * len(selected_pms)) - # Define column widths - col_widths = [pkg_col_width] + [version_col_width] * len(selected_pms) + # If terminal is too narrow for the table, use list format instead + if terminal_width < min_required_width and len(selected_pms) > 1: + print_list_format(output, headers, selected_pms, args, pkg_availability) + return - # Adjust to fit terminal if needed - while sum(col_widths) + (3 * (len(col_widths) - 1)) > terminal_width and min(col_widths) > min_col_width: - max_idx = col_widths.index(max(col_widths)) - col_widths[max_idx] -= 1 + # Calculate column widths + padding = 1 # Space on each side of content - # Calculate total table width for separators + # Package column width + pkg_col_width = min(min_pkg_width + 4, max(min_pkg_width, terminal_width // (len(selected_pms) + 3))) + + # PM column widths (divide remaining space equally) + remaining_width = terminal_width - pkg_col_width - (3 * len(selected_pms)) + pm_col_width = max(min_pm_width, remaining_width // len(selected_pms)) + + col_widths = [pkg_col_width] + [pm_col_width] * len(selected_pms) + + # Print header row + header_cells = [] + for i, header in enumerate(headers): + text = header.center(col_widths[i] - (2 * padding)) + cell = " " * padding + colorize(text, 'header') + " " * padding + header_cells.append(cell) + + print(" | ".join(header_cells)) + + # Print separator line total_width = sum(col_widths) + (3 * (len(col_widths) - 1)) - - # Print headers (centered) - header_row = [] - for idx, header in enumerate(headers): - header_text = header.center(col_widths[idx] - (padding * 2)) - padded_header = " " * padding + colorize(header_text, 'header') + " " * padding - header_row.append(padded_header.ljust(col_widths[idx])) - - print(" | ".join(header_row)) - - # Print main separator line print("-" * total_width) - # Process and print each package row - for pkg_index, pkg in enumerate(args.packages): - versions = output.get(pkg, {pm: 'Not found' for pm in selected_pms}) + # Print each package row + for pkg_idx, pkg in enumerate(args.packages): + versions = output.get(pkg, {}) - # Prepare content for each cell - cells_content = [] + # First collect all data for this package + package_data = [] # Package name (first column) - cells_content.append(colorize(pkg, 'bold')) + package_data.append([colorize(pkg, pkg_availability[pkg])]) - # Version information (remaining columns) + # Version data for each package manager for pm in selected_pms: version = versions.get(pm, 'Not found') - color = 'green' if version != 'Not found' else 'red' - if 'AUR' in version: color = 'yellow' - # Apply color to the entire string at once - cells_content.append(colorize(version, color)) - - # Apply wrapping to each cell based on column width - wrapped_cells = [] - for idx, content in enumerate(cells_content): - # Use raw text for wrapping calculation, but keep the colored text for display - raw_text = content - if colorize.is_tty: - # Remove ANSI color codes for length calculation - raw_text = re.sub(r'\033\[\d+m', '', content) - avail_width = col_widths[idx] - (padding * 2) + if version == 'Not found': + package_data.append([colorize('-', 'red')]) + continue - # Wrap text without color codes - wrapped_lines = textwrap.wrap( - raw_text, - width=avail_width, - break_long_words=False, - break_on_hyphens=True - ) or [''] + # Extract version number and repositories + version_parts = [] + match = re.match(r'(.*?)\s+\((.*)\)$', version) - # If we have color codes, we need to re-apply coloring to each wrapped line - if colorize.is_tty: - color = None - if idx == 0: # Package name - color = 'bold' - else: # Version info - if 'Not found' in raw_text: - color = 'red' - elif 'AUR' in raw_text: - color = 'yellow' - else: - color = 'green' + if match: + ver_num, repos = match.groups() + version_parts.append(colorize(ver_num, 'green')) - # Apply the same color to each wrapped line - wrapped_lines = [colorize(line, color) for line in wrapped_lines] - - wrapped_cells.append(wrapped_lines) - - # Determine how many lines this row needs - max_lines = max(len(cell) for cell in wrapped_cells) - - # Print each line of the row - for line_idx in range(max_lines): - row_parts = [] - for col_idx, cell_lines in enumerate(wrapped_cells): - if line_idx < len(cell_lines): - content = cell_lines[line_idx] - if line_idx > 0: # For continuation lines - # Indent continuation lines for better readability - # We can't simply add characters to colored text, need to handle differently - if colorize.is_tty: - # Need to extract the color codes and apply them after indent - plain_content = re.sub(r'\033\[\d+m', '', content) - color_prefix = content[:content.find(plain_content)] - color_suffix = COLORS['reset'] - content = color_prefix + " " + plain_content + color_suffix - else: - content = " " + content - - # Calculate proper padding with color codes - if colorize.is_tty: - plain_content = re.sub(r'\033\[\d+m', '', content) - color_parts = content.split(plain_content) - padded = " " * padding + content + " " * padding - - # For colored text, we need to calculate padding differently - plain_padded = " " * padding + plain_content + " " * padding - padding_needed = col_widths[col_idx] - len(plain_padded) - if padding_needed > 0: - padded += " " * padding_needed - else: - padded = (" " * padding + content + " " * padding).ljust(col_widths[col_idx]) - - row_parts.append(padded) + # Format repositories + repo_lines = [] + repo_text = "(" + repos + ")" + + # Wrap repository text if needed + avail_width = col_widths[len(package_data)] - (2 * padding) + if len(repo_text) <= avail_width: + repo_lines.append(colorize(repo_text, 'green')) else: - # Empty space for this cell in this line - row_parts.append(" " * col_widths[col_idx]) - - print(" | ".join(row_parts)) + # Handle wrapping for repositories + repo_parts = repos.split(', ') + current_line = "(" + + for repo in repo_parts: + if len(current_line) + len(repo) + 2 <= avail_width: + if current_line != "(": + current_line += ", " + current_line += repo + else: + if current_line != "(": + current_line += ")" + repo_lines.append(colorize(current_line, 'green')) + current_line = " " + repo + + if current_line != "(": + current_line += ")" if not current_line.startswith(" ") else "" + repo_lines.append(colorize(current_line, 'green')) + + # Combined version and repo lines + package_data.append([version_parts[0]] + repo_lines) + else: + # Simple version string + package_data.append([colorize(version, 'green')]) - # Add a separator between packages (but not after the last one) - if pkg_index < len(args.packages) - 1: - # Use a lighter separator for between packages + # Determine max number of lines needed + max_lines = max(len(column) for column in package_data) + + # Print all lines for this package + for line_idx in range(max_lines): + row_cells = [] + + for col_idx, col_data in enumerate(package_data): + if line_idx < len(col_data): + # Actual content + content = col_data[line_idx] + content_plain = re.sub(r'\033\[\d+m', '', content) + + # Calculate padding + left_pad = padding + right_pad = max(0, col_widths[col_idx] - len(content_plain) - left_pad) + + cell = " " * left_pad + content + " " * right_pad + else: + # Empty cell + cell = " " * col_widths[col_idx] + + row_cells.append(cell) + + print(" | ".join(row_cells)) + + # Add separator between packages + if pkg_idx < len(args.packages) - 1: print("ยท" * total_width) +def print_list_format(output, headers, selected_pms, args, pkg_availability): + """Print packages in a vertical list format for narrow terminals""" + terminal_width = shutil.get_terminal_size().columns + + for pkg_idx, pkg in enumerate(args.packages): + pkg_color = pkg_availability[pkg] + versions = output.get(pkg, {}) + + # Print package header with color based on availability + print(f"\n{colorize('Package:', 'bold')} {colorize(pkg, pkg_color)}") + print("-" * min(40, terminal_width - 2)) + + # Print versions for each package manager + for pm in selected_pms: + pm_name = headers[selected_pms.index(pm) + 1] # Get friendly display name + version = versions.get(pm, 'Not found') + + if version == 'Not found': + print(f"{colorize(pm_name, 'header')}: {colorize('-', 'red')}") + else: + # Extract version and repo information + match = re.match(r'(.*?)\s+\((.*)\)$', version) + if match: + ver_num, repos = match.groups() + + # Handle long repository lists with wrapping + if len(pm_name) + len(ver_num) + len(repos) + 5 > terminal_width: + print(f"{colorize(pm_name, 'header')}: {colorize(ver_num, 'green')}") + # Wrap repositories with proper indentation + wrapper = textwrap.TextWrapper( + width=terminal_width - 4, + initial_indent=" ", + subsequent_indent=" " + ) + wrapped = wrapper.fill(f"({repos})") + print(colorize(wrapped, 'green')) + else: + print(f"{colorize(pm_name, 'header')}: {colorize(ver_num, 'green')} ({repos})") + else: + print(f"{colorize(pm_name, 'header')}: {colorize(version, 'green')}") + + # Add separator between packages + if pkg_idx < len(args.packages) - 1: + print("\n" + "-" * min(40, terminal_width - 2)) + def main(): parser = argparse.ArgumentParser(description='Package search tool') parser.add_argument('--all', action='store_true')