feat: preserve block titles when merging borders (#1977)

Resolves #1939
This commit is contained in:
Jagoda Estera Ślązak 2025-07-13 00:45:56 +02:00 committed by GitHub
parent 821611f76f
commit 017af11b2b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 100 additions and 14 deletions

View File

@ -300,15 +300,20 @@ impl MergeStrategy {
/// [Box Drawing Unicode block]: https://en.wikipedia.org/wiki/Box_Drawing
/// [`Cell::merge_symbol`]: crate::buffer::Cell::merge_symbol
pub fn merge<'a>(self, prev: &'a str, next: &'a str) -> &'a str {
let (Ok(prev_symbol), Ok(next_symbol)) =
(BorderSymbol::from_str(prev), BorderSymbol::from_str(next))
else {
// Replace should always just return the last symbol.
if self == Self::Replace {
return next;
};
if let Ok(merged) = prev_symbol.merge(next_symbol, self).try_into() {
return merged;
}
next
match (BorderSymbol::from_str(prev), BorderSymbol::from_str(next)) {
(Ok(prev_symbol), Ok(next_symbol)) => prev_symbol
.merge(next_symbol, self)
.try_into()
.unwrap_or(next),
// Non-border symbols take precedence in strategies other than Replace.
(Err(_), Ok(_)) => prev,
(_, Err(_)) => next,
}
}
}
@ -523,7 +528,6 @@ macro_rules! define_symbols {
}
define_symbols!(
" " => (Nothing, Nothing, Nothing, Nothing),
"" => (Plain, Nothing, Plain, Nothing),
"" => (Thick, Nothing, Thick, Nothing),
"" => (Nothing, Plain, Nothing, Plain),
@ -666,7 +670,7 @@ mod tests {
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", " ",
"", "", "", "", "", "", " ", "a", "b",
];
for a in symbols {
@ -695,8 +699,8 @@ mod tests {
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge(" ", ""), "");
assert_eq!(strategy.merge("", " "), "");
assert_eq!(strategy.merge(" ", ""), " ");
assert_eq!(strategy.merge("", " "), " ");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
@ -704,6 +708,9 @@ mod tests {
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", "a"), "a");
assert_eq!(strategy.merge("a", ""), "a");
assert_eq!(strategy.merge("a", "b"), "b");
}
#[test]
@ -711,7 +718,7 @@ mod tests {
let strategy = MergeStrategy::Fuzzy;
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge(" ", ""), "");
assert_eq!(strategy.merge(" ", ""), " ");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
@ -725,8 +732,8 @@ mod tests {
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge(" ", ""), "");
assert_eq!(strategy.merge("", " "), "");
assert_eq!(strategy.merge(" ", ""), " ");
assert_eq!(strategy.merge("", " "), " ");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
@ -734,5 +741,8 @@ mod tests {
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", ""), "");
assert_eq!(strategy.merge("", "a"), "a");
assert_eq!(strategy.merge("a", ""), "a");
assert_eq!(strategy.merge("a", "b"), "b");
}
}

View File

@ -1969,6 +1969,82 @@ mod tests {
pretty_assertions::assert_eq!(Buffer::with_lines(expected.lines()), buffer);
}
#[rstest]
#[case::replace(MergeStrategy::Replace, Buffer::with_lines([
"┏block top━━┓",
"┃ ┃",
"┗━━━━━━━━━━━┛",
"│ │",
"└───────────┘",
])
)]
#[case::replace(MergeStrategy::Exact, Buffer::with_lines([
"┏block top━━┓",
"┃ ┃",
"┡block btm━━┩",
"│ │",
"└───────────┘",
])
)]
#[case::replace(MergeStrategy::Fuzzy, Buffer::with_lines([
"┏block top━━┓",
"┃ ┃",
"┡block btm━━┩",
"│ │",
"└───────────┘",
])
)]
fn merged_titles_bottom_first(#[case] strategy: MergeStrategy, #[case] expected: Buffer) {
let mut buffer = Buffer::empty(Rect::new(0, 0, 13, 5));
Block::bordered()
.title("block btm")
.render(Rect::new(0, 2, 13, 3), &mut buffer);
Block::bordered()
.title("block top")
.border_type(BorderType::Thick)
.merge_borders(strategy)
.render(Rect::new(0, 0, 13, 3), &mut buffer);
assert_eq!(buffer, expected);
}
#[rstest]
#[case::replace(MergeStrategy::Replace, Buffer::with_lines([
"┏block top━━┓",
"┃ ┃",
"┌block btm──┐",
"│ │",
"└───────────┘",
])
)]
#[case::replace(MergeStrategy::Exact, Buffer::with_lines([
"┏block top━━┓",
"┃ ┃",
"┞block btm──┦",
"│ │",
"└───────────┘",
])
)]
#[case::replace(MergeStrategy::Fuzzy, Buffer::with_lines([
"┏block top━━┓",
"┃ ┃",
"┞block btm──┦",
"│ │",
"└───────────┘",
])
)]
fn merged_titles_top_first(#[case] strategy: MergeStrategy, #[case] expected: Buffer) {
let mut buffer = Buffer::empty(Rect::new(0, 0, 13, 5));
Block::bordered()
.title("block top")
.border_type(BorderType::Thick)
.render(Rect::new(0, 0, 13, 3), &mut buffer);
Block::bordered()
.title("block btm")
.merge_borders(strategy)
.render(Rect::new(0, 2, 13, 3), &mut buffer);
assert_eq!(buffer, expected);
}
#[test]
fn left_titles() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 1));