mirror of
https://github.com/jj-vcs/jj.git
synced 2025-12-23 06:01:01 +00:00
templater: add optional ellipsis arg to truncate template functions
If an ellipsis arg is given to the truncate_* template functions, append (or prepend) the ellipsis when the template content is truncated to fit the maximum width. Fixes #5085.
This commit is contained in:
parent
8144ffcaf8
commit
975e1e1e24
4 changed files with 363 additions and 52 deletions
|
|
@ -16,6 +16,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||
|
||||
### New features
|
||||
|
||||
* Template functions `truncate_start()` and `truncate_end()` gained an optional
|
||||
`ellipsis` parameter; passing this prepends or appends the ellipsis to the
|
||||
content if it is truncated to fit the maximum width.
|
||||
|
||||
### Fixed bugs
|
||||
|
||||
* `jj status` now shows untracked files under untracked directories.
|
||||
|
|
|
|||
|
|
@ -1494,22 +1494,32 @@ fn builtin_functions<'a, L: TemplateLanguage<'a> + ?Sized>() -> TemplateBuildFun
|
|||
map.insert(
|
||||
"truncate_start",
|
||||
|language, diagnostics, build_ctx, function| {
|
||||
let [width_node, content_node] = function.expect_exact_arguments()?;
|
||||
let ([width_node, content_node], [ellipsis_node]) =
|
||||
function.expect_named_arguments(&["", "", "ellipsis"])?;
|
||||
let width = expect_usize_expression(language, diagnostics, build_ctx, width_node)?;
|
||||
let content =
|
||||
expect_template_expression(language, diagnostics, build_ctx, content_node)?;
|
||||
let template = new_truncate_template(content, width, text_util::write_truncated_start);
|
||||
let ellipsis = ellipsis_node
|
||||
.map(|node| expect_template_expression(language, diagnostics, build_ctx, node))
|
||||
.transpose()?;
|
||||
let template =
|
||||
new_truncate_template(content, ellipsis, width, text_util::write_truncated_start);
|
||||
Ok(L::wrap_template(template))
|
||||
},
|
||||
);
|
||||
map.insert(
|
||||
"truncate_end",
|
||||
|language, diagnostics, build_ctx, function| {
|
||||
let [width_node, content_node] = function.expect_exact_arguments()?;
|
||||
let ([width_node, content_node], [ellipsis_node]) =
|
||||
function.expect_named_arguments(&["", "", "ellipsis"])?;
|
||||
let width = expect_usize_expression(language, diagnostics, build_ctx, width_node)?;
|
||||
let content =
|
||||
expect_template_expression(language, diagnostics, build_ctx, content_node)?;
|
||||
let template = new_truncate_template(content, width, text_util::write_truncated_end);
|
||||
let ellipsis = ellipsis_node
|
||||
.map(|node| expect_template_expression(language, diagnostics, build_ctx, node))
|
||||
.transpose()?;
|
||||
let template =
|
||||
new_truncate_template(content, ellipsis, width, text_util::write_truncated_end);
|
||||
Ok(L::wrap_template(template))
|
||||
},
|
||||
);
|
||||
|
|
@ -1643,18 +1653,29 @@ where
|
|||
|
||||
fn new_truncate_template<'a, W>(
|
||||
content: Box<dyn Template + 'a>,
|
||||
ellipsis: Option<Box<dyn Template + 'a>>,
|
||||
width: Box<dyn TemplateProperty<Output = usize> + 'a>,
|
||||
write_truncated: W,
|
||||
) -> Box<dyn Template + 'a>
|
||||
where
|
||||
W: Fn(&mut dyn Formatter, &FormatRecorder, usize) -> io::Result<usize> + 'a,
|
||||
W: Fn(&mut dyn Formatter, &FormatRecorder, &FormatRecorder, usize) -> io::Result<usize> + 'a,
|
||||
{
|
||||
let default_ellipsis = FormatRecorder::with_data("");
|
||||
let template = ReformatTemplate::new(content, move |formatter, recorded| {
|
||||
let width = match width.extract() {
|
||||
Ok(width) => width,
|
||||
Err(err) => return formatter.handle_error(err),
|
||||
};
|
||||
write_truncated(formatter.as_mut(), recorded, width)?;
|
||||
let mut ellipsis_recorder;
|
||||
let recorded_ellipsis = if let Some(ellipsis) = &ellipsis {
|
||||
let rewrap = formatter.rewrap_fn();
|
||||
ellipsis_recorder = FormatRecorder::new();
|
||||
ellipsis.format(&mut rewrap(&mut ellipsis_recorder))?;
|
||||
&ellipsis_recorder
|
||||
} else {
|
||||
&default_ellipsis
|
||||
};
|
||||
write_truncated(formatter.as_mut(), recorded, recorded_ellipsis, width)?;
|
||||
Ok(())
|
||||
});
|
||||
Box::new(template)
|
||||
|
|
|
|||
|
|
@ -230,18 +230,39 @@ fn count_start_zero_width_chars_bytes(text: &[u8]) -> usize {
|
|||
pub fn write_truncated_start(
|
||||
formatter: &mut dyn Formatter,
|
||||
recorded_content: &FormatRecorder,
|
||||
recorded_ellipsis: &FormatRecorder,
|
||||
max_width: usize,
|
||||
) -> io::Result<usize> {
|
||||
let data = recorded_content.data();
|
||||
let (start, truncated_width) = truncate_start_pos_bytes(data, max_width);
|
||||
let data_width = String::from_utf8_lossy(data).width();
|
||||
let ellipsis_data = recorded_ellipsis.data();
|
||||
let ellipsis_width = String::from_utf8_lossy(ellipsis_data).width();
|
||||
|
||||
let (start, mut truncated_width) = if data_width > max_width {
|
||||
truncate_start_pos_bytes(data, max_width.saturating_sub(ellipsis_width))
|
||||
} else {
|
||||
(0, data_width)
|
||||
};
|
||||
|
||||
let mut replay_truncated = |recorded: &FormatRecorder, truncated_start: usize| {
|
||||
recorded.replay_with(formatter, |formatter, range| {
|
||||
let start = cmp::max(range.start, truncated_start);
|
||||
if start < range.end {
|
||||
formatter.write_all(&recorded.data()[start..range.end])?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
};
|
||||
|
||||
if data_width > max_width {
|
||||
// The ellipsis itself may be larger than max_width, so maybe truncate it too.
|
||||
let (start, ellipsis_width) = truncate_start_pos_bytes(ellipsis_data, max_width);
|
||||
let truncated_start = start + count_start_zero_width_chars_bytes(&ellipsis_data[start..]);
|
||||
truncated_width += ellipsis_width;
|
||||
replay_truncated(recorded_ellipsis, truncated_start)?;
|
||||
}
|
||||
let truncated_start = start + count_start_zero_width_chars_bytes(&data[start..]);
|
||||
recorded_content.replay_with(formatter, |formatter, range| {
|
||||
let start = cmp::max(range.start, truncated_start);
|
||||
if start < range.end {
|
||||
formatter.write_all(&data[start..range.end])?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
replay_truncated(recorded_content, truncated_start)?;
|
||||
Ok(truncated_width)
|
||||
}
|
||||
|
||||
|
|
@ -252,17 +273,37 @@ pub fn write_truncated_start(
|
|||
pub fn write_truncated_end(
|
||||
formatter: &mut dyn Formatter,
|
||||
recorded_content: &FormatRecorder,
|
||||
recorded_ellipsis: &FormatRecorder,
|
||||
max_width: usize,
|
||||
) -> io::Result<usize> {
|
||||
let data = recorded_content.data();
|
||||
let (truncated_end, truncated_width) = truncate_end_pos_bytes(data, max_width);
|
||||
recorded_content.replay_with(formatter, |formatter, range| {
|
||||
let end = cmp::min(range.end, truncated_end);
|
||||
if range.start < end {
|
||||
formatter.write_all(&data[range.start..end])?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
let data_width = String::from_utf8_lossy(data).width();
|
||||
let ellipsis_data = recorded_ellipsis.data();
|
||||
let ellipsis_width = String::from_utf8_lossy(ellipsis_data).width();
|
||||
|
||||
let (truncated_end, mut truncated_width) = if data_width > max_width {
|
||||
truncate_end_pos_bytes(data, max_width.saturating_sub(ellipsis_width))
|
||||
} else {
|
||||
(data.len(), data_width)
|
||||
};
|
||||
|
||||
let mut replay_truncated = |recorded: &FormatRecorder, truncated_end: usize| {
|
||||
recorded.replay_with(formatter, |formatter, range| {
|
||||
let end = cmp::min(range.end, truncated_end);
|
||||
if range.start < end {
|
||||
formatter.write_all(&recorded.data()[range.start..end])?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
};
|
||||
|
||||
replay_truncated(recorded_content, truncated_end)?;
|
||||
if data_width > max_width {
|
||||
// The ellipsis itself may be larger than max_width, so maybe truncate it too.
|
||||
let (truncated_end, ellipsis_width) = truncate_end_pos_bytes(ellipsis_data, max_width);
|
||||
truncated_width += ellipsis_width;
|
||||
replay_truncated(recorded_ellipsis, truncated_end)?;
|
||||
}
|
||||
Ok(truncated_width)
|
||||
}
|
||||
|
||||
|
|
@ -693,6 +734,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_write_truncated_labeled() {
|
||||
let ellipsis_recorder = FormatRecorder::new();
|
||||
let mut recorder = FormatRecorder::new();
|
||||
for (label, word) in [("red", "foo"), ("cyan", "bar")] {
|
||||
recorder.push_label(label).unwrap();
|
||||
|
|
@ -702,128 +744,368 @@ mod tests {
|
|||
|
||||
// Truncate start
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 6).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 6).map(|_| ())
|
||||
}),
|
||||
@"[38;5;1mfoo[39m[38;5;6mbar[39m"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 5).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 5).map(|_| ())
|
||||
}),
|
||||
@"[38;5;1moo[39m[38;5;6mbar[39m"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 3).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 3).map(|_| ())
|
||||
}),
|
||||
@"[38;5;6mbar[39m"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 2).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 2).map(|_| ())
|
||||
}),
|
||||
@"[38;5;6mar[39m"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 0).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 0).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
|
||||
// Truncate end
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_end(formatter, &recorder, 6).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 6).map(|_| ())
|
||||
}),
|
||||
@"[38;5;1mfoo[39m[38;5;6mbar[39m"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_end(formatter, &recorder, 5).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 5).map(|_| ())
|
||||
}),
|
||||
@"[38;5;1mfoo[39m[38;5;6mba[39m"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_end(formatter, &recorder, 3).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 3).map(|_| ())
|
||||
}),
|
||||
@"[38;5;1mfoo[39m"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_end(formatter, &recorder, 2).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 2).map(|_| ())
|
||||
}),
|
||||
@"[38;5;1mfo[39m"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_end(formatter, &recorder, 0).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 0).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_truncated_non_ascii_chars() {
|
||||
let ellipsis_recorder = FormatRecorder::new();
|
||||
let mut recorder = FormatRecorder::new();
|
||||
write!(recorder, "a\u{300}bc\u{300}一二三").unwrap();
|
||||
|
||||
// Truncate start
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 1).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 1).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 2).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 2).map(|_| ())
|
||||
}),
|
||||
@"三"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 3).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 3).map(|_| ())
|
||||
}),
|
||||
@"三"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 6).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 6).map(|_| ())
|
||||
}),
|
||||
@"一二三"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 7).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 7).map(|_| ())
|
||||
}),
|
||||
@"c̀一二三"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 9).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 9).map(|_| ())
|
||||
}),
|
||||
@"àbc̀一二三"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 10).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 10).map(|_| ())
|
||||
}),
|
||||
@"àbc̀一二三"
|
||||
);
|
||||
|
||||
// Truncate end
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_end(formatter, &recorder, 1).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 1).map(|_| ())
|
||||
}),
|
||||
@"à"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_end(formatter, &recorder, 4).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 4).map(|_| ())
|
||||
}),
|
||||
@"àbc̀"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_end(formatter, &recorder, 5).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 5).map(|_| ())
|
||||
}),
|
||||
@"àbc̀一"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_end(formatter, &recorder, 9).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 9).map(|_| ())
|
||||
}),
|
||||
@"àbc̀一二三"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_end(formatter, &recorder, 10).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 10).map(|_| ())
|
||||
}),
|
||||
@"àbc̀一二三"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_truncated_empty_content() {
|
||||
let ellipsis_recorder = FormatRecorder::new();
|
||||
let recorder = FormatRecorder::new();
|
||||
|
||||
// Truncate start
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 0).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 0).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_start(formatter, &recorder, 1).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 1).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
|
||||
// Truncate end
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_end(formatter, &recorder, 0).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 0).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| write_truncated_end(formatter, &recorder, 1).map(|_| ())),
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 1).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_truncated_ellipsis_labeled() {
|
||||
let ellipsis_recorder = FormatRecorder::with_data("..");
|
||||
let mut recorder = FormatRecorder::new();
|
||||
for (label, word) in [("red", "foo"), ("cyan", "bar")] {
|
||||
recorder.push_label(label).unwrap();
|
||||
write!(recorder, "{word}").unwrap();
|
||||
recorder.pop_label().unwrap();
|
||||
}
|
||||
|
||||
// Truncate start
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 6).map(|_| ())
|
||||
}),
|
||||
@"[38;5;1mfoo[39m[38;5;6mbar[39m"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 5).map(|_| ())
|
||||
}),
|
||||
@"..[38;5;6mbar[39m"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 3).map(|_| ())
|
||||
}),
|
||||
@"..[38;5;6mr[39m"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 2).map(|_| ())
|
||||
}),
|
||||
@".."
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 1).map(|_| ())
|
||||
}),
|
||||
@"."
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 0).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
|
||||
// Truncate end
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 6).map(|_| ())
|
||||
}),
|
||||
@"[38;5;1mfoo[39m[38;5;6mbar[39m"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 5).map(|_| ())
|
||||
}),
|
||||
@"[38;5;1mfoo[39m.."
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 3).map(|_| ())
|
||||
}),
|
||||
@"[38;5;1mf[39m.."
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 2).map(|_| ())
|
||||
}),
|
||||
@".."
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 1).map(|_| ())
|
||||
}),
|
||||
@"."
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 0).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_truncated_ellipsis_non_ascii_chars() {
|
||||
let ellipsis_recorder = FormatRecorder::with_data("..");
|
||||
let mut recorder = FormatRecorder::new();
|
||||
write!(recorder, "a\u{300}bc\u{300}一二三").unwrap();
|
||||
|
||||
// Truncate start
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 1).map(|_| ())
|
||||
}),
|
||||
@"."
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 2).map(|_| ())
|
||||
}),
|
||||
@".."
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 4).map(|_| ())
|
||||
}),
|
||||
@"..三"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 7).map(|_| ())
|
||||
}),
|
||||
@"..二三"
|
||||
);
|
||||
|
||||
// Truncate end
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 1).map(|_| ())
|
||||
}),
|
||||
@"."
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 4).map(|_| ())
|
||||
}),
|
||||
@"àb.."
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 5).map(|_| ())
|
||||
}),
|
||||
@"àbc̀.."
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 9).map(|_| ())
|
||||
}),
|
||||
@"àbc̀一二三"
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 10).map(|_| ())
|
||||
}),
|
||||
@"àbc̀一二三"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_truncated_ellipsis_empty_content() {
|
||||
let ellipsis_recorder = FormatRecorder::with_data("..");
|
||||
let recorder = FormatRecorder::new();
|
||||
|
||||
// Truncate start, empty content
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 0).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_start(formatter, &recorder, &ellipsis_recorder, 1).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
|
||||
// Truncate end
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 0).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
insta::assert_snapshot!(
|
||||
format_colored(|formatter| {
|
||||
write_truncated_end(formatter, &recorder, &ellipsis_recorder, 1).map(|_| ())
|
||||
}),
|
||||
@""
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,10 +59,14 @@ The following functions are defined.
|
|||
content by adding both leading and trailing fill characters. If an odd number
|
||||
of fill characters are needed, the trailing fill will be one longer than the
|
||||
leading fill. The `content` shouldn't have newline characters.
|
||||
* `truncate_start(width: Integer, content: Template)`: Truncate `content` by
|
||||
removing leading characters. The `content` shouldn't have newline character.
|
||||
* `truncate_end(width: Integer, content: Template)`: Truncate `content` by
|
||||
removing trailing characters. The `content` shouldn't have newline character.
|
||||
* `truncate_start(width: Integer, content: Template[, ellipsis: Template])`:
|
||||
Truncate `content` by removing leading characters. The `content` shouldn't
|
||||
have newline character. If `ellipsis` is provided and `content` was truncated,
|
||||
prepend the `ellipsis` to the result.
|
||||
* `truncate_end(width: Integer, content: Template[, ellipsis: Template])`:
|
||||
Truncate `content` by removing trailing characters. The `content` shouldn't
|
||||
have newline character. If `ellipsis` is provided and `content` was truncated,
|
||||
append the `ellipsis` to the result.
|
||||
* `label(label: Template, content: Template) -> Template`: Apply label to
|
||||
the content. The `label` is evaluated as a space-separated string.
|
||||
* `raw_escape_sequence(content: Template) -> Template`: Preserves any escape
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue