From 4fc78b3e69705357e58cb84e28b8c1e9213b5a22 Mon Sep 17 00:00:00 2001 From: Bryan Gardiner Date: Thu, 26 Sep 2024 20:14:03 -0700 Subject: [PATCH] Add selectable version highlighting; use a default red/green scheme. --- src/nvd | 109 ++++++++++++++++++++++++++++++++++++------------------ src/nvd.1 | 17 +++++++++ 2 files changed, 91 insertions(+), 35 deletions(-) diff --git a/src/nvd b/src/nvd index 94f3a59..8fa4bd0 100755 --- a/src/nvd +++ b/src/nvd @@ -108,6 +108,18 @@ ALPHANUM_RE = re.compile(r"\w") # Alternatively [0-9a-zA-Z] would work. DIGIT_RE = re.compile("[0-9]") +VERSION_HIGHLIGHT_NONE = "none" +VERSION_HIGHLIGHT_BOLD = "bold" +VERSION_HIGHLIGHT_XMAS = "xmas" + +VERSION_HIGHLIGHT_DEFAULT = VERSION_HIGHLIGHT_XMAS + +ALL_VERSION_HIGHLIGHTS = ( + VERSION_HIGHLIGHT_NONE, + VERSION_HIGHLIGHT_BOLD, + VERSION_HIGHLIGHT_XMAS, +) + def raise_arg(e): raise e @@ -507,8 +519,10 @@ def parse_pname_version(path: str) -> tuple[str, Optional[str]]: def render_versions( version_list: list[Version], *, - colour: bool = False, + colour: bool, + version_highlight: str, highlight_from_pos: Optional[int] = None, + is_right: bool = False, ) -> str: if version_list == []: return "" @@ -530,10 +544,21 @@ def render_versions( if highlight_from_pos is None: text = sgr(SGR_FG + SGR_YELLOW) + text + sgr(SGR_RESET) else: + if version_highlight == VERSION_HIGHLIGHT_XMAS: + sgr_highlight = sgr(SGR_FG + (SGR_GREEN if is_right else SGR_RED)) + elif version_highlight == VERSION_HIGHLIGHT_BOLD: + sgr_highlight = sgr(SGR_BOLD) + elif version_highlight == VERSION_HIGHLIGHT_NONE: + sgr_highlight = "" + else: + raise RuntimeError( + f"Internal error, unknown version_highlight: {version_highlight!r}" + ) + text = \ sgr(SGR_FG + SGR_YELLOW) \ + text[0:highlight_from_pos] \ - + sgr(SGR_BOLD) \ + + sgr_highlight \ + text[highlight_from_pos:] \ + sgr(SGR_RESET) @@ -546,14 +571,15 @@ def render_versions( return ", ".join(items) def print_packages( - *, - pnames: Collection[str], - sort_comparator: Optional[PackageListEntryComparator], - selected_sets: PackageSetPair, - left_package_set: PackageSet, - right_package_set: Optional[PackageSet] = None, - fixed_install_state: Optional[str] = None, - no_version_suffix_highlight: bool = False, + *, + pnames: Collection[str], + sort_comparator: Optional[PackageListEntryComparator], + version_highlight: str, + selected_sets: PackageSetPair, + left_package_set: PackageSet, + right_package_set: Optional[PackageSet] = None, + fixed_install_state: Optional[str] = None, + no_version_suffix_highlight: bool = False, ): assert (right_package_set is None) != (fixed_install_state is None), \ "Expect either one package set, or a fixed install state." @@ -621,7 +647,7 @@ def print_packages( pname_sgr = bold_if_selected_sgr + sgr(SGR_FG + SGR_GREEN) versions_common_prefix_len: Optional[int] - if have_single_version or no_version_suffix_highlight: + if have_single_version or version_highlight == VERSION_HIGHLIGHT_NONE: versions_common_prefix_len = None else: versions_common_prefix_len = find_common_version_prefix_lists( @@ -642,13 +668,16 @@ def print_packages( render_versions( left_versions, colour=True, + version_highlight=version_highlight, highlight_from_pos=versions_common_prefix_len, ), "" if have_single_version else ( " -> " + render_versions( right_versions, colour=True, + version_highlight=version_highlight, highlight_from_pos=versions_common_prefix_len, + is_right=True, ) ), )) @@ -847,6 +876,7 @@ def run_list( only_selected, name_patterns, sort_comparator: PackageListEntryComparator, + version_highlight: str, ): path = Path(root) @@ -882,15 +912,16 @@ def run_list( right_package_set=None, fixed_install_state=INST_INSTALLED, sort_comparator=sort_comparator, + version_highlight=version_highlight, ) def run_diff( - *, - root1, - root2, - no_version_suffix_highlight: bool, - sort_comparator: PackageListEntryComparator, - only_selected: bool, + *, + root1, + root2, + sort_comparator: PackageListEntryComparator, + only_selected: bool, + version_highlight: str, ): left_path = Path(root1) right_path = Path(root2) @@ -963,8 +994,8 @@ def run_diff( selected_sets=selected_sets, left_package_set=left_closure_set, right_package_set=right_closure_set, - no_version_suffix_highlight=no_version_suffix_highlight, sort_comparator=sort_comparator, + version_highlight=version_highlight, ) # Announce specific changes for packages whose versions haven't changed. @@ -977,6 +1008,7 @@ def run_diff( left_package_set=left_closure_set, fixed_install_state=INST_CHANGED, sort_comparator=sort_comparator, + version_highlight=version_highlight, ) # Announce added packages. @@ -989,6 +1021,7 @@ def run_diff( left_package_set=right_closure_set, # Yes, this is correct. fixed_install_state=INST_ADDED, sort_comparator=sort_comparator, + version_highlight=version_highlight, ) # Announce removed packages. @@ -1001,6 +1034,7 @@ def run_diff( left_package_set=left_closure_set, fixed_install_state=INST_REMOVED, sort_comparator=sort_comparator, + version_highlight=version_highlight, ) if not any_changes_displayed: @@ -1030,13 +1064,13 @@ def run_diff( ) def run_history( - *, - profile, - minimum_version: int, - list_oldest: bool, - no_version_suffix_highlight: bool, - sort_comparator: PackageListEntryComparator, - only_selected: bool, + *, + profile, + minimum_version: int, + list_oldest: bool, + sort_comparator: PackageListEntryComparator, + only_selected: bool, + version_highlight: str, ): profile_path = Path(profile) @@ -1065,7 +1099,8 @@ def run_history( root=oldest_profile.path(), only_selected=only_selected, name_patterns=[], - sort_comparator=sort_comparator + sort_comparator=sort_comparator, + version_highlight=version_highlight, ) else: print(f"Contents of profile version {oldest_profile.version()} were omitted, use --list-oldest to show them.") @@ -1076,9 +1111,9 @@ def run_history( run_diff( root1=base_profile.path(), root2=displayed_profile.path(), - no_version_suffix_highlight=no_version_suffix_highlight, sort_comparator=sort_comparator, only_selected=only_selected, + version_highlight=version_highlight, ) def parse_args(): @@ -1112,6 +1147,11 @@ def parse_args(): default="auto", help="Controls use of colour; one of 'auto', 'never', 'always'.") + parser.add_argument( + "--version-highlight", + default=VERSION_HIGHLIGHT_DEFAULT, + help="Colour scheme to apply to changed parts of versions.") + parser.add_argument( "--nix-bin-dir", default=None, @@ -1124,10 +1164,6 @@ def parse_args(): help="Diff two Nix store paths and their closures.") add_selected_arg(diff_parser) add_sort_arg(diff_parser) - diff_parser.add_argument( - "--no-version-suffix-highlight", - action="store_true", - help="Disable highlighting of the changed portions of versions.") diff_parser.add_argument( "root1", help="The first Nix store path to compare.") @@ -1170,10 +1206,6 @@ def parse_args(): action="store_true", help="Additionally list all packages in the first displayed profile." ) - history_parser.add_argument( - "--no-version-suffix-highlight", - action="store_true", - help="Disable highlighting of the changed portions of versions.") return parser.parse_args() @@ -1204,6 +1236,13 @@ def main(): INST_DOWNGRADED = sgr(SGR_FG + SGR_BRIGHT + SGR_YELLOW) + INST_DOWNGRADED INST_CHANGED = sgr(SGR_FG + SGR_BRIGHT + SGR_MAGENTA) + INST_CHANGED + if ( + "version_highlight" in args + and args["version_highlight"] not in ALL_VERSION_HIGHLIGHTS + ): + sys.stderr.write(f"nvd: Error, unknown --version-highlight: '{args['version_highlight']}'\n") + sys.exit(1) + if "sort" in args: args["sort_comparator"] = PackageListEntryCombinedComparator.from_str(args["sort"]) del args["sort"] diff --git a/src/nvd.1 b/src/nvd.1 index 4dae794..9f639ff 100644 --- a/src/nvd.1 +++ b/src/nvd.1 @@ -58,6 +58,8 @@ nvd \- Nix/NixOS package version diff tool .RS .B [ --color=(auto|always|never) ] .br +.B [ --version-highlight=(xmas|bold|none) ] +.br .B [ --nix-bin-dir= ] .RE .SH DESCRIPTION @@ -157,6 +159,21 @@ forces colour to always be used, and passing .B never forces colour to never be used. .TP +--version-highlight=(xmas|bold|none) +The colour scheme to use to highlight the changed parts of version strings, when +versions are being compared (with the +.B diff +and +.B history +commands). Highlighting is applied to the tail part of each version after +whatever prefix is common to all versions present. The default +.B xmas +colours old versions red and new versions green. The value +.B bold +highlights the changed parts of versions in bold. The value +.B none +disables highlighting. +.TP --nix-bin-dir= An optional path to a directory containing .B nix,