From b41bdb548ada2faa99f97d5c1c00d422a7bc1e2f Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sun, 5 Mar 2023 12:13:18 +0900 Subject: [PATCH] templater: generalize IndentTemplate as reformatting template New fill() function will also use this struct. --- src/template_parser.rs | 21 ++++++++++---- src/templater.rs | 66 +++++++++++++++++++----------------------- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/template_parser.rs b/src/template_parser.rs index 6bb4e0a1e..55dc4780e 100644 --- a/src/template_parser.rs +++ b/src/template_parser.rs @@ -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)?; diff --git a/src/templater.rs b/src/templater.rs index c2ffec9b3..8d89d8fd3 100644 --- a/src/templater.rs +++ b/src/templater.rs @@ -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 { 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 { - prefix: S, - content: T, -} - -impl IndentTemplate { - pub fn new(prefix: S, content: T) -> Self - where - S: Template, - T: Template, - { - IndentTemplate { prefix, content } - } -} - -impl Template for IndentTemplate -where - S: Template, - T: Template, -{ - 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 { content: T, labels: L, @@ -195,6 +159,34 @@ impl> Template for ConcatTemplate { } } +/// Renders the content to buffer, and transforms it without losing labels. +pub struct ReformatTemplate { + content: T, + reformat: F, +} + +impl ReformatTemplate { + pub fn new(content: T, reformat: F) -> Self + where + T: Template, + F: Fn(&C, &mut dyn Formatter, &FormatRecorder) -> io::Result<()>, + { + ReformatTemplate { content, reformat } + } +} + +impl Template for ReformatTemplate +where + T: Template, + 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 { separator: S,