From ece3e9dbfb35e768dd7ed0af957d50df950e342e Mon Sep 17 00:00:00 2001 From: Rootiest Date: Tue, 8 Sep 2026 04:08:00 -0400 Subject: [PATCH] feat(config-settings): add __config_settings_diff_redraw --- functions/__config_settings_diff_redraw.fish | 47 ++++++++++++++++++++ tests/functional.fish | 16 +++++++ 2 files changed, 63 insertions(+) create mode 100644 functions/__config_settings_diff_redraw.fish diff --git a/functions/__config_settings_diff_redraw.fish b/functions/__config_settings_diff_redraw.fish new file mode 100644 index 0000000..f5d9621 --- /dev/null +++ b/functions/__config_settings_diff_redraw.fish @@ -0,0 +1,47 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# SYNOPSIS +# __config_settings_diff_redraw +# +# DESCRIPTION +# Rewrites an on-screen panel frame in place, touching only the lines +# that changed. Assumes the cursor is already positioned at the top-left +# of the frame (the caller moves it there with a plain \e[A -- no +# \e[J -- before calling this). Line count in old_joined and new_joined +# must be equal; a caller facing a height or width change should use the +# existing full erase+redraw path instead of calling this. +# +# Unchanged lines advance the cursor with a bare newline, leaving +# whatever is already on screen untouched. Changed lines clear just that +# line (\e[2K), return to its start (\r), print the new content, and +# advance (\n). This is what removes the erase-then-redraw flicker: the +# screen is never blanked, only the handful of lines that actually +# differ are ever touched, and each of those is cleared and rewritten in +# the same breath rather than blanked-then-paused-then-filled. +# +# ARGUMENTS +# old_joined Previous frame, lines joined with \n +# new_joined New frame, lines joined with \n (same line count as old) +# +# EXIT STATUS +# 0 Always +# +# RETURNS +# The ANSI sequence needed to turn the old frame into the new one, +# printed to stdout +# +# EXAMPLE +# __config_settings_diff_redraw (string join \n -- $prev_frame) \ +# (string join \n -- $new_frame) +function __config_settings_diff_redraw + set -l old (string split \n -- $argv[1]) + set -l new (string split \n -- $argv[2]) + for i in (seq (count $new)) + if test "$old[$i]" = "$new[$i]" + printf '\n' + else + printf '\e[2K\r%s\n' $new[$i] + end + end +end diff --git a/tests/functional.fish b/tests/functional.fish index e49e8e6..5292520 100644 --- a/tests/functional.fish +++ b/tests/functional.fish @@ -125,6 +125,22 @@ function test_draw_line_count_matches_panel_h end end +function test_diff_redraw_unchanged_lines_are_bare_newlines + functions -q __config_settings_diff_redraw; or return 1 + set -l old (string join \n -- AAA BBB CCC | string collect) + set -l new (string join \n -- AAA BBB CCC | string collect) + set -l out (__config_settings_diff_redraw "$old" "$new" | string collect -N) + test "$out" = \n\n\n +end + +function test_diff_redraw_changed_line_is_cleared_and_rewritten + functions -q __config_settings_diff_redraw; or return 1 + set -l old (string join \n -- AAA BBB CCC | string collect) + set -l new (string join \n -- AAA XYZ CCC | string collect) + set -l out (__config_settings_diff_redraw "$old" "$new" | string collect -N) + test "$out" = \n\e\[2K\rXYZ\n\n +end + function functional_test_main set -l names (functions -a | string match 'test_*' | sort) set -l failed 0