templater: generalize IndentTemplate as reformatting template

New fill() function will also use this struct.
This commit is contained in:
Yuya Nishihara 2023-03-05 12:13:18 +09:00
parent 2af2aca7ff
commit b41bdb548a
2 changed files with 45 additions and 42 deletions

View file

@ -25,11 +25,11 @@ use pest_derive::Parser;
use thiserror::Error;
use crate::templater::{
ConcatTemplate, ConditionalTemplate, FormattablePropertyListTemplate, IndentTemplate,
IntoTemplate, LabelTemplate, Literal, PlainTextFormattedProperty, SeparateTemplate, Template,
TemplateFunction, TemplateProperty, TimestampRange,
ConcatTemplate, ConditionalTemplate, FormattablePropertyListTemplate, IntoTemplate,
LabelTemplate, Literal, PlainTextFormattedProperty, ReformatTemplate, SeparateTemplate,
Template, TemplateFunction, TemplateProperty, TimestampRange,
};
use crate::time_util;
use crate::{text_util, time_util};
#[derive(Parser)]
#[grammar = "template.pest"]
@ -1075,7 +1075,18 @@ fn build_global_function<'a, L: TemplateLanguage<'a>>(
let [prefix_node, content_node] = expect_exact_arguments(function)?;
let prefix = build_expression(language, prefix_node)?.into_template();
let content = build_expression(language, content_node)?.into_template();
language.wrap_template(IndentTemplate::new(prefix, content))
let template = ReformatTemplate::new(content, move |context, formatter, recorded| {
text_util::write_indented(formatter, recorded, |formatter| {
// If Template::format() returned a custom error type, we would need to
// handle template evaluation error out of this closure:
// prefix.format(context, &mut prefix_recorder)?;
// write_indented(formatter, recorded, |formatter| {
// prefix_recorder.replay(formatter)
// })
prefix.format(context, formatter)
})
});
language.wrap_template(template)
}
"label" => {
let [label_node, content_node] = expect_exact_arguments(function)?;

View file

@ -17,7 +17,7 @@ use std::io;
use jujutsu_lib::backend::{Signature, Timestamp};
use crate::formatter::{FormatRecorder, Formatter, PlainTextFormatter};
use crate::{text_util, time_util};
use crate::time_util;
pub trait Template<C> {
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()>;
@ -115,42 +115,6 @@ impl Template<()> for i64 {
}
}
/// Indents each line by the given prefix.
pub struct IndentTemplate<S, T> {
prefix: S,
content: T,
}
impl<S, T> IndentTemplate<S, T> {
pub fn new<C>(prefix: S, content: T) -> Self
where
S: Template<C>,
T: Template<C>,
{
IndentTemplate { prefix, content }
}
}
impl<C, S, T> Template<C> for IndentTemplate<S, T>
where
S: Template<C>,
T: Template<C>,
{
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
let mut recorder = FormatRecorder::new();
self.content.format(context, &mut recorder)?;
text_util::write_indented(formatter, &recorder, |formatter| {
// If Template::format() returned a custom error type, we would need to
// handle template evaluation error out of this closure:
// prefix.format(context, &mut prefix_recorder)?;
// write_indented(formatter, recorded, |formatter| {
// prefix_recorder.replay(formatter)
// })
self.prefix.format(context, formatter)
})
}
}
pub struct LabelTemplate<T, L> {
content: T,
labels: L,
@ -195,6 +159,34 @@ impl<C, T: Template<C>> Template<C> for ConcatTemplate<T> {
}
}
/// Renders the content to buffer, and transforms it without losing labels.
pub struct ReformatTemplate<T, F> {
content: T,
reformat: F,
}
impl<T, F> ReformatTemplate<T, F> {
pub fn new<C>(content: T, reformat: F) -> Self
where
T: Template<C>,
F: Fn(&C, &mut dyn Formatter, &FormatRecorder) -> io::Result<()>,
{
ReformatTemplate { content, reformat }
}
}
impl<C, T, F> Template<C> for ReformatTemplate<T, F>
where
T: Template<C>,
F: Fn(&C, &mut dyn Formatter, &FormatRecorder) -> io::Result<()>,
{
fn format(&self, context: &C, formatter: &mut dyn Formatter) -> io::Result<()> {
let mut recorder = FormatRecorder::new();
self.content.format(context, &mut recorder)?;
(self.reformat)(context, formatter, &recorder)
}
}
/// Like `ConcatTemplate`, but inserts a separator between non-empty templates.
pub struct SeparateTemplate<S, T> {
separator: S,