mirror of
https://gitlab.com/khumba/nvd.git
synced 2024-11-14 00:49:26 +01:00
Add selectable version highlighting; use a default red/green scheme.
This commit is contained in:
parent
f40b73c812
commit
4fc78b3e69
2 changed files with 91 additions and 35 deletions
109
src/nvd
109
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]")
|
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)
|
||||||
|
|
||||||
|
@ -546,14 +571,15 @@ def render_versions(
|
||||||
return ", ".join(items)
|
return ", ".join(items)
|
||||||
|
|
||||||
def print_packages(
|
def print_packages(
|
||||||
*,
|
*,
|
||||||
pnames: Collection[str],
|
pnames: Collection[str],
|
||||||
sort_comparator: Optional[PackageListEntryComparator],
|
sort_comparator: Optional[PackageListEntryComparator],
|
||||||
selected_sets: PackageSetPair,
|
version_highlight: str,
|
||||||
left_package_set: PackageSet,
|
selected_sets: PackageSetPair,
|
||||||
right_package_set: Optional[PackageSet] = None,
|
left_package_set: PackageSet,
|
||||||
fixed_install_state: Optional[str] = None,
|
right_package_set: Optional[PackageSet] = None,
|
||||||
no_version_suffix_highlight: bool = False,
|
fixed_install_state: Optional[str] = None,
|
||||||
|
no_version_suffix_highlight: bool = False,
|
||||||
):
|
):
|
||||||
assert (right_package_set is None) != (fixed_install_state is None), \
|
assert (right_package_set is None) != (fixed_install_state is None), \
|
||||||
"Expect either one package set, or a fixed install state."
|
"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)
|
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:
|
||||||
|
@ -1030,13 +1064,13 @@ def run_diff(
|
||||||
)
|
)
|
||||||
|
|
||||||
def run_history(
|
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"]
|
||||||
|
|
17
src/nvd.1
17
src/nvd.1
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue