mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-29 07:59:00 +00:00
templater: add Template::has_content() to filter out empty expressions
This allows us to insert a separator between non-empty template fragments. Since FormattablePropertyTemplate::has_content() needs to extract a TemplateProperty, using this function means the property function will be evaluated twice, one by .has_content() and later by .format(). The cost is basically the same as 'if(prop, " " prop)'.
This commit is contained in:
parent
5850575d53
commit
84ee0edc51
2 changed files with 61 additions and 0 deletions
|
@ -103,6 +103,10 @@ fn cmd_op_log(
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_content(&self, _: &Operation) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
let template = OpTemplate {
|
||||
relative_timestamps: command.settings().relative_timestamps(),
|
||||
|
|
|
@ -25,12 +25,18 @@ use crate::time_util;
|
|||
|
||||
pub trait Template<C> {
|
||||
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()>;
|
||||
/// Returns true if `format()` will generate output other than labels.
|
||||
fn has_content(&self, context: &C) -> bool;
|
||||
}
|
||||
|
||||
impl<C, T: Template<C> + ?Sized> Template<C> for Box<T> {
|
||||
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
|
||||
<T as Template<C>>::format(self, context, formatter)
|
||||
}
|
||||
|
||||
fn has_content(&self, context: &C) -> bool {
|
||||
<T as Template<C>>::has_content(self, context)
|
||||
}
|
||||
}
|
||||
|
||||
impl Template<()> for Signature {
|
||||
|
@ -41,24 +47,40 @@ impl Template<()> for Signature {
|
|||
write!(formatter, ">")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_content(&self, _: &()) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Template<()> for String {
|
||||
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
||||
formatter.write_str(self)
|
||||
}
|
||||
|
||||
fn has_content(&self, _: &()) -> bool {
|
||||
!self.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl Template<()> for Timestamp {
|
||||
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
||||
formatter.write_str(&time_util::format_absolute_timestamp(self))
|
||||
}
|
||||
|
||||
fn has_content(&self, _: &()) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Template<()> for bool {
|
||||
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
||||
formatter.write_str(if *self { "true" } else { "false" })
|
||||
}
|
||||
|
||||
fn has_content(&self, _: &()) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LabelTemplate<T, L> {
|
||||
|
@ -92,6 +114,10 @@ where
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_content(&self, context: &C) -> bool {
|
||||
self.content.has_content(context)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ListTemplate<T>(pub Vec<T>);
|
||||
|
@ -103,6 +129,10 @@ impl<C, T: Template<C>> Template<C> for ListTemplate<T> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_content(&self, context: &C) -> bool {
|
||||
self.0.iter().any(|template| template.has_content(context))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TemplateProperty<C> {
|
||||
|
@ -126,6 +156,10 @@ impl<C, O: Template<()>> Template<C> for Literal<O> {
|
|||
fn format(&self, _context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
|
||||
self.0.format(&(), formatter)
|
||||
}
|
||||
|
||||
fn has_content(&self, _context: &C) -> bool {
|
||||
self.0.has_content(&())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, O: Clone> TemplateProperty<C> for Literal<O> {
|
||||
|
@ -171,6 +205,11 @@ where
|
|||
let template = self.property.extract(context);
|
||||
template.format(&(), formatter)
|
||||
}
|
||||
|
||||
fn has_content(&self, context: &C) -> bool {
|
||||
let template = self.property.extract(context);
|
||||
template.has_content(&())
|
||||
}
|
||||
}
|
||||
|
||||
/// Adapter to turn template back to string property.
|
||||
|
@ -367,6 +406,16 @@ where
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_content(&self, context: &C) -> bool {
|
||||
if self.condition.extract(context) {
|
||||
self.true_template.has_content(context)
|
||||
} else if let Some(false_template) = &self.false_template {
|
||||
false_template.has_content(context)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: If needed, add a ContextualTemplateFunction where the function also
|
||||
|
@ -459,6 +508,10 @@ impl Template<()> for CommitOrChangeId<'_> {
|
|||
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
||||
formatter.write_str(&self.hex())
|
||||
}
|
||||
|
||||
fn has_content(&self, _: &()) -> bool {
|
||||
!self.id_bytes.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// This function supports short `total_len` by ensuring that the entire
|
||||
|
@ -517,4 +570,8 @@ impl Template<()> for IdWithHighlightedPrefix {
|
|||
formatter.with_label("prefix", |fmt| fmt.write_str(&self.prefix))?;
|
||||
formatter.with_label("rest", |fmt| fmt.write_str(&self.rest))
|
||||
}
|
||||
|
||||
fn has_content(&self, _: &()) -> bool {
|
||||
!self.prefix.is_empty() || !self.rest.is_empty()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue