Add selectable version highlighting; use a default red/green scheme.

This commit is contained in:
Bryan Gardiner 2024-09-26 20:14:03 -07:00
parent f40b73c812
commit 4fc78b3e69
No known key found for this signature in database
GPG key ID: 53EFBCA063E6183C
2 changed files with 91 additions and 35 deletions

71
src/nvd
View file

@ -108,6 +108,18 @@ ALPHANUM_RE = re.compile(r"\w") # Alternatively [0-9a-zA-Z] would work.
DIGIT_RE = re.compile("[0-9]") 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): def raise_arg(e):
raise e raise e
@ -507,8 +519,10 @@ def parse_pname_version(path: str) -> tuple[str, Optional[str]]:
def render_versions( def render_versions(
version_list: list[Version], version_list: list[Version],
*, *,
colour: bool = False, colour: bool,
version_highlight: str,
highlight_from_pos: Optional[int] = None, highlight_from_pos: Optional[int] = None,
is_right: bool = False,
) -> str: ) -> str:
if version_list == []: if version_list == []:
return "<no versions>" return "<no versions>"
@ -530,10 +544,21 @@ def render_versions(
if highlight_from_pos is None: if highlight_from_pos is None:
text = sgr(SGR_FG + SGR_YELLOW) + text + sgr(SGR_RESET) text = sgr(SGR_FG + SGR_YELLOW) + text + sgr(SGR_RESET)
else: 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 = \ text = \
sgr(SGR_FG + SGR_YELLOW) \ sgr(SGR_FG + SGR_YELLOW) \
+ text[0:highlight_from_pos] \ + text[0:highlight_from_pos] \
+ sgr(SGR_BOLD) \ + sgr_highlight \
+ text[highlight_from_pos:] \ + text[highlight_from_pos:] \
+ sgr(SGR_RESET) + sgr(SGR_RESET)
@ -549,6 +574,7 @@ def print_packages(
*, *,
pnames: Collection[str], pnames: Collection[str],
sort_comparator: Optional[PackageListEntryComparator], sort_comparator: Optional[PackageListEntryComparator],
version_highlight: str,
selected_sets: PackageSetPair, selected_sets: PackageSetPair,
left_package_set: PackageSet, left_package_set: PackageSet,
right_package_set: Optional[PackageSet] = None, right_package_set: Optional[PackageSet] = None,
@ -621,7 +647,7 @@ def print_packages(
pname_sgr = bold_if_selected_sgr + sgr(SGR_FG + SGR_GREEN) pname_sgr = bold_if_selected_sgr + sgr(SGR_FG + SGR_GREEN)
versions_common_prefix_len: Optional[int] 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 versions_common_prefix_len = None
else: else:
versions_common_prefix_len = find_common_version_prefix_lists( versions_common_prefix_len = find_common_version_prefix_lists(
@ -642,13 +668,16 @@ def print_packages(
render_versions( render_versions(
left_versions, left_versions,
colour=True, colour=True,
version_highlight=version_highlight,
highlight_from_pos=versions_common_prefix_len, highlight_from_pos=versions_common_prefix_len,
), ),
"" if have_single_version else ( "" if have_single_version else (
" -> " + render_versions( " -> " + render_versions(
right_versions, right_versions,
colour=True, colour=True,
version_highlight=version_highlight,
highlight_from_pos=versions_common_prefix_len, highlight_from_pos=versions_common_prefix_len,
is_right=True,
) )
), ),
)) ))
@ -847,6 +876,7 @@ def run_list(
only_selected, only_selected,
name_patterns, name_patterns,
sort_comparator: PackageListEntryComparator, sort_comparator: PackageListEntryComparator,
version_highlight: str,
): ):
path = Path(root) path = Path(root)
@ -882,15 +912,16 @@ def run_list(
right_package_set=None, right_package_set=None,
fixed_install_state=INST_INSTALLED, fixed_install_state=INST_INSTALLED,
sort_comparator=sort_comparator, sort_comparator=sort_comparator,
version_highlight=version_highlight,
) )
def run_diff( def run_diff(
*, *,
root1, root1,
root2, root2,
no_version_suffix_highlight: bool,
sort_comparator: PackageListEntryComparator, sort_comparator: PackageListEntryComparator,
only_selected: bool, only_selected: bool,
version_highlight: str,
): ):
left_path = Path(root1) left_path = Path(root1)
right_path = Path(root2) right_path = Path(root2)
@ -963,8 +994,8 @@ def run_diff(
selected_sets=selected_sets, selected_sets=selected_sets,
left_package_set=left_closure_set, left_package_set=left_closure_set,
right_package_set=right_closure_set, right_package_set=right_closure_set,
no_version_suffix_highlight=no_version_suffix_highlight,
sort_comparator=sort_comparator, sort_comparator=sort_comparator,
version_highlight=version_highlight,
) )
# Announce specific changes for packages whose versions haven't changed. # Announce specific changes for packages whose versions haven't changed.
@ -977,6 +1008,7 @@ def run_diff(
left_package_set=left_closure_set, left_package_set=left_closure_set,
fixed_install_state=INST_CHANGED, fixed_install_state=INST_CHANGED,
sort_comparator=sort_comparator, sort_comparator=sort_comparator,
version_highlight=version_highlight,
) )
# Announce added packages. # Announce added packages.
@ -989,6 +1021,7 @@ def run_diff(
left_package_set=right_closure_set, # Yes, this is correct. left_package_set=right_closure_set, # Yes, this is correct.
fixed_install_state=INST_ADDED, fixed_install_state=INST_ADDED,
sort_comparator=sort_comparator, sort_comparator=sort_comparator,
version_highlight=version_highlight,
) )
# Announce removed packages. # Announce removed packages.
@ -1001,6 +1034,7 @@ def run_diff(
left_package_set=left_closure_set, left_package_set=left_closure_set,
fixed_install_state=INST_REMOVED, fixed_install_state=INST_REMOVED,
sort_comparator=sort_comparator, sort_comparator=sort_comparator,
version_highlight=version_highlight,
) )
if not any_changes_displayed: if not any_changes_displayed:
@ -1034,9 +1068,9 @@ def run_history(
profile, profile,
minimum_version: int, minimum_version: int,
list_oldest: bool, list_oldest: bool,
no_version_suffix_highlight: bool,
sort_comparator: PackageListEntryComparator, sort_comparator: PackageListEntryComparator,
only_selected: bool, only_selected: bool,
version_highlight: str,
): ):
profile_path = Path(profile) profile_path = Path(profile)
@ -1065,7 +1099,8 @@ def run_history(
root=oldest_profile.path(), root=oldest_profile.path(),
only_selected=only_selected, only_selected=only_selected,
name_patterns=[], name_patterns=[],
sort_comparator=sort_comparator sort_comparator=sort_comparator,
version_highlight=version_highlight,
) )
else: else:
print(f"Contents of profile version {oldest_profile.version()} were omitted, use --list-oldest to show them.") 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( run_diff(
root1=base_profile.path(), root1=base_profile.path(),
root2=displayed_profile.path(), root2=displayed_profile.path(),
no_version_suffix_highlight=no_version_suffix_highlight,
sort_comparator=sort_comparator, sort_comparator=sort_comparator,
only_selected=only_selected, only_selected=only_selected,
version_highlight=version_highlight,
) )
def parse_args(): def parse_args():
@ -1112,6 +1147,11 @@ def parse_args():
default="auto", default="auto",
help="Controls use of colour; one of 'auto', 'never', 'always'.") 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( parser.add_argument(
"--nix-bin-dir", "--nix-bin-dir",
default=None, default=None,
@ -1124,10 +1164,6 @@ def parse_args():
help="Diff two Nix store paths and their closures.") help="Diff two Nix store paths and their closures.")
add_selected_arg(diff_parser) add_selected_arg(diff_parser)
add_sort_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( diff_parser.add_argument(
"root1", "root1",
help="The first Nix store path to compare.") help="The first Nix store path to compare.")
@ -1170,10 +1206,6 @@ def parse_args():
action="store_true", action="store_true",
help="Additionally list all packages in the first displayed profile." 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() return parser.parse_args()
@ -1204,6 +1236,13 @@ def main():
INST_DOWNGRADED = sgr(SGR_FG + SGR_BRIGHT + SGR_YELLOW) + INST_DOWNGRADED INST_DOWNGRADED = sgr(SGR_FG + SGR_BRIGHT + SGR_YELLOW) + INST_DOWNGRADED
INST_CHANGED = sgr(SGR_FG + SGR_BRIGHT + SGR_MAGENTA) + INST_CHANGED 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: if "sort" in args:
args["sort_comparator"] = PackageListEntryCombinedComparator.from_str(args["sort"]) args["sort_comparator"] = PackageListEntryCombinedComparator.from_str(args["sort"])
del args["sort"] del args["sort"]

View file

@ -58,6 +58,8 @@ nvd \- Nix/NixOS package version diff tool
.RS .RS
.B [ --color=(auto|always|never) ] .B [ --color=(auto|always|never) ]
.br .br
.B [ --version-highlight=(xmas|bold|none) ]
.br
.B [ --nix-bin-dir=<path> ] .B [ --nix-bin-dir=<path> ]
.RE .RE
.SH DESCRIPTION .SH DESCRIPTION
@ -157,6 +159,21 @@ forces colour to always be used, and passing
.B never .B never
forces colour to never be used. forces colour to never be used.
.TP .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=<path> --nix-bin-dir=<path>
An optional path to a directory containing An optional path to a directory containing
.B nix, .B nix,