mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-27 06:23:18 +00:00
templater: add wrapper that turns template back to string property
I have no idea whether or not any template expressions are intentionally allowed as a label, but it makes sense to write something like 'label("phase-" phase, ...)' (if we had a phase keyword.) So I decided to add .into_plain_text() instead of stricter .try_into_string().
This commit is contained in:
parent
a42ce8773c
commit
1911734acc
3 changed files with 77 additions and 11 deletions
|
@ -22,12 +22,12 @@ use pest::iterators::{Pair, Pairs};
|
|||
use pest::Parser;
|
||||
use pest_derive::Parser;
|
||||
|
||||
use crate::formatter::PlainTextFormatter;
|
||||
use crate::templater::{
|
||||
BranchProperty, CommitOrChangeId, ConditionalTemplate, DynamicLabelTemplate,
|
||||
FormattablePropertyTemplate, GitHeadProperty, GitRefsProperty, IdWithHighlightedPrefix,
|
||||
IsWorkingCopyProperty, LabelTemplate, ListTemplate, Literal, TagProperty, Template,
|
||||
TemplateFunction, TemplateProperty, TemplatePropertyFn, WorkingCopiesProperty,
|
||||
IsWorkingCopyProperty, LabelTemplate, ListTemplate, Literal, PlainTextFormattedProperty,
|
||||
TagProperty, Template, TemplateFunction, TemplateProperty, TemplatePropertyFn,
|
||||
WorkingCopiesProperty,
|
||||
};
|
||||
use crate::{cli_util, time_util};
|
||||
|
||||
|
@ -98,6 +98,13 @@ impl<'a, I: 'a> Property<'a, I> {
|
|||
}
|
||||
}
|
||||
|
||||
fn into_plain_text(self) -> Box<dyn TemplateProperty<I, Output = String> + 'a> {
|
||||
match self {
|
||||
Property::String(property) => property,
|
||||
_ => Box::new(PlainTextFormattedProperty::new(self.into_template())),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_template(self) -> Box<dyn Template<I> + 'a> {
|
||||
fn wrap<'a, I: 'a, O: Template<()> + 'a>(
|
||||
property: Box<dyn TemplateProperty<I, Output = O> + 'a>,
|
||||
|
@ -141,6 +148,13 @@ impl<'a, C: 'a> Expression<'a, C> {
|
|||
}
|
||||
}
|
||||
|
||||
fn into_plain_text(self) -> Box<dyn TemplateProperty<C, Output = String> + 'a> {
|
||||
match self {
|
||||
Expression::Property(PropertyAndLabels(property, _)) => property.into_plain_text(),
|
||||
Expression::Template(template) => Box::new(PlainTextFormattedProperty::new(template)),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_template(self) -> Box<dyn Template<C> + 'a> {
|
||||
match self {
|
||||
Expression::Property(property_labels) => property_labels.into_template(),
|
||||
|
@ -330,8 +344,8 @@ fn parse_commit_term<'a>(
|
|||
match name.as_str() {
|
||||
"label" => {
|
||||
let label_pair = args.next().unwrap();
|
||||
let label_template =
|
||||
parse_commit_template_rule(repo, workspace_id, label_pair).into_template();
|
||||
let label_property = parse_commit_template_rule(repo, workspace_id, label_pair)
|
||||
.into_plain_text();
|
||||
let arg_template = match args.next() {
|
||||
None => panic!("label() requires two arguments"),
|
||||
Some(pair) => pair,
|
||||
|
@ -342,11 +356,8 @@ fn parse_commit_term<'a>(
|
|||
let content = parse_commit_template_rule(repo, workspace_id, arg_template)
|
||||
.into_template();
|
||||
let get_labels = move |commit: &Commit| -> Vec<String> {
|
||||
let mut buf = vec![];
|
||||
let mut formatter = PlainTextFormatter::new(&mut buf);
|
||||
label_template.format(commit, &mut formatter).unwrap();
|
||||
String::from_utf8(buf)
|
||||
.unwrap()
|
||||
label_property
|
||||
.extract(commit)
|
||||
.split_whitespace()
|
||||
.map(ToString::to_string)
|
||||
.collect()
|
||||
|
|
|
@ -21,7 +21,7 @@ use jujutsu_lib::commit::Commit;
|
|||
use jujutsu_lib::op_store::WorkspaceId;
|
||||
use jujutsu_lib::repo::RepoRef;
|
||||
|
||||
use crate::formatter::Formatter;
|
||||
use crate::formatter::{Formatter, PlainTextFormatter};
|
||||
use crate::time_util;
|
||||
|
||||
pub trait Template<C> {
|
||||
|
@ -204,6 +204,30 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Adapter to turn template back to string property.
|
||||
pub struct PlainTextFormattedProperty<T> {
|
||||
template: T,
|
||||
}
|
||||
|
||||
impl<T> PlainTextFormattedProperty<T> {
|
||||
pub fn new(template: T) -> Self {
|
||||
PlainTextFormattedProperty { template }
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, T: Template<C>> TemplateProperty<C> for PlainTextFormattedProperty<T> {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &C) -> Self::Output {
|
||||
let mut output = vec![];
|
||||
self.template
|
||||
.format(context, &mut PlainTextFormatter::new(&mut output))
|
||||
.expect("write() to PlainTextFormatter should never fail");
|
||||
// TODO: Use from_utf8_lossy() if we added template that embeds file content
|
||||
String::from_utf8(output).expect("template output should be utf-8 bytes")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WorkingCopiesProperty<'a> {
|
||||
pub repo: RepoRef<'a>,
|
||||
}
|
||||
|
|
|
@ -116,6 +116,37 @@ fn test_templater_string_method() {
|
|||
insta::assert_snapshot!(render(r#""foo\nbar".first_line()"#), @"foo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_templater_label_function() {
|
||||
let test_env = TestEnvironment::default();
|
||||
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
|
||||
let repo_path = test_env.env_root().join("repo");
|
||||
let render = |template| {
|
||||
test_env.jj_cmd_success(
|
||||
&repo_path,
|
||||
&[
|
||||
"log",
|
||||
"--color=always",
|
||||
"--no-graph",
|
||||
"-r@-",
|
||||
"-T",
|
||||
template,
|
||||
],
|
||||
)
|
||||
};
|
||||
|
||||
// Literal
|
||||
insta::assert_snapshot!(render(r#"label("error", "text")"#), @"[38;5;1mtext[39m");
|
||||
|
||||
// Evaluated property
|
||||
insta::assert_snapshot!(
|
||||
render(r#"label("error".first_line(), "text")"#), @"[38;5;1mtext[39m");
|
||||
|
||||
// Template
|
||||
insta::assert_snapshot!(
|
||||
render(r#"label(if(empty, "error", "warning"), "text")"#), @"[38;5;1mtext[39m");
|
||||
}
|
||||
|
||||
fn get_template_output(
|
||||
test_env: &TestEnvironment,
|
||||
repo_path: &Path,
|
||||
|
|
Loading…
Reference in a new issue