mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-19 19:08:08 +00:00
generic_templater: clone &Context to Self_ property like other languages
This prepares for removal of TemplateLanguage::Context type. "C: Clone" trait bounds looked messy, but they can be removed soon.
This commit is contained in:
parent
915e75efc0
commit
017026148b
3 changed files with 55 additions and 39 deletions
|
@ -26,7 +26,7 @@ use crate::command_error::{user_error, CommandError};
|
|||
use crate::config::{AnnotatedValue, ConfigSource};
|
||||
use crate::generic_templater::GenericTemplateLanguage;
|
||||
use crate::template_builder::TemplateLanguage as _;
|
||||
use crate::templater::TemplatePropertyFn;
|
||||
use crate::templater::TemplateFunction;
|
||||
use crate::ui::Ui;
|
||||
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
|
@ -184,25 +184,28 @@ pub(crate) fn cmd_config(
|
|||
}
|
||||
}
|
||||
|
||||
// AnnotatedValue will be cloned internally in the templater. If the cloning
|
||||
// cost matters, wrap it with Rc.
|
||||
fn config_template_language() -> GenericTemplateLanguage<'static, AnnotatedValue> {
|
||||
type L = GenericTemplateLanguage<'static, AnnotatedValue>;
|
||||
fn prop_fn<R, F: Fn(&AnnotatedValue) -> R>(f: F) -> TemplatePropertyFn<F> {
|
||||
TemplatePropertyFn(f)
|
||||
}
|
||||
let mut language = GenericTemplateLanguage::new();
|
||||
let mut language = L::new();
|
||||
// "name" instead of "path" to avoid confusion with the source file path
|
||||
language.add_keyword("name", || {
|
||||
let property = prop_fn(|annotated| Ok(annotated.path.join(".")));
|
||||
Ok(L::wrap_string(property))
|
||||
language.add_keyword("name", |self_property| {
|
||||
let out_property =
|
||||
TemplateFunction::new(self_property, |annotated| Ok(annotated.path.join(".")));
|
||||
Ok(L::wrap_string(out_property))
|
||||
});
|
||||
language.add_keyword("value", || {
|
||||
language.add_keyword("value", |self_property| {
|
||||
// TODO: would be nice if we can provide raw dynamically-typed value
|
||||
let property = prop_fn(|annotated| Ok(serialize_config_value(&annotated.value)));
|
||||
Ok(L::wrap_string(property))
|
||||
let out_property = TemplateFunction::new(self_property, |annotated| {
|
||||
Ok(serialize_config_value(&annotated.value))
|
||||
});
|
||||
Ok(L::wrap_string(out_property))
|
||||
});
|
||||
language.add_keyword("overridden", || {
|
||||
let property = prop_fn(|annotated| Ok(annotated.is_overridden));
|
||||
Ok(L::wrap_boolean(property))
|
||||
language.add_keyword("overridden", |self_property| {
|
||||
let out_property =
|
||||
TemplateFunction::new(self_property, |annotated| Ok(annotated.is_overridden));
|
||||
Ok(L::wrap_boolean(out_property))
|
||||
});
|
||||
language
|
||||
}
|
||||
|
|
|
@ -19,19 +19,19 @@ use crate::template_builder::{
|
|||
TemplateLanguage,
|
||||
};
|
||||
use crate::template_parser::{self, FunctionCallNode, TemplateParseResult};
|
||||
use crate::templater::{Template, TemplateProperty};
|
||||
use crate::templater::{Template, TemplateProperty, TemplatePropertyFn};
|
||||
|
||||
/// General-purpose template language for basic value types.
|
||||
///
|
||||
/// This template language only supports the core template property types (plus
|
||||
/// the context type `C`.) The context type `C` is usually a tuple or struct of
|
||||
/// value types. Keyword functions need to be registered to extract properties
|
||||
/// from the context object.
|
||||
pub struct GenericTemplateLanguage<'a, C> {
|
||||
/// value types. It's cloned several times internally. Keyword functions need to
|
||||
/// be registered to extract properties from the context object.
|
||||
pub struct GenericTemplateLanguage<'a, C: Clone> {
|
||||
build_fn_table: GenericTemplateBuildFnTable<'a, C>,
|
||||
}
|
||||
|
||||
impl<'a, C> GenericTemplateLanguage<'a, C> {
|
||||
impl<'a, C: Clone> GenericTemplateLanguage<'a, C> {
|
||||
/// Sets up environment with no keywords.
|
||||
///
|
||||
/// New keyword functions can be registered by `add_keyword()`.
|
||||
|
@ -55,32 +55,33 @@ impl<'a, C> GenericTemplateLanguage<'a, C> {
|
|||
///
|
||||
/// A keyword function returns `Self::Property`, which is basically a
|
||||
/// closure tagged by its return type. The inner closure is usually wrapped
|
||||
/// by `TemplatePropertyFn`.
|
||||
/// by `TemplateFunction`.
|
||||
///
|
||||
/// ```ignore
|
||||
/// language.add_keyword("name", || {
|
||||
/// let property = TemplatePropertyFn(|v: &C| Ok(v.to_string()));
|
||||
/// Ok(GenericTemplateLanguage::wrap_string(property))
|
||||
/// language.add_keyword("name", |self_property| {
|
||||
/// let out_property = TemplateFunction::new(self_property, |v| Ok(v.to_string()));
|
||||
/// Ok(GenericTemplateLanguage::wrap_string(out_property))
|
||||
/// });
|
||||
/// ```
|
||||
pub fn add_keyword<F>(&mut self, name: &'static str, build: F)
|
||||
where
|
||||
F: Fn() -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>> + 'a,
|
||||
F: Fn(
|
||||
Box<dyn TemplateProperty<C, Output = C> + 'a>,
|
||||
) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
|
||||
+ 'a,
|
||||
{
|
||||
self.build_fn_table.keywords.insert(name, Box::new(build));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: 'a> TemplateLanguage<'a> for GenericTemplateLanguage<'a, C> {
|
||||
impl<'a, C: Clone + 'a> TemplateLanguage<'a> for GenericTemplateLanguage<'a, C> {
|
||||
type Context = C;
|
||||
type Property = GenericTemplatePropertyKind<'a, C>;
|
||||
|
||||
template_builder::impl_core_wrap_property_fns!('a, GenericTemplatePropertyKind::Core);
|
||||
|
||||
fn build_self(&self) -> Self::Property {
|
||||
// No need to clone the context object because there are no other
|
||||
// objects of "Self" type, and the context is available everywhere.
|
||||
GenericTemplatePropertyKind::Self_
|
||||
Self::wrap_self(TemplatePropertyFn(|context: &C| Ok(context.clone())))
|
||||
}
|
||||
|
||||
fn build_function(
|
||||
|
@ -103,48 +104,56 @@ impl<'a, C: 'a> TemplateLanguage<'a> for GenericTemplateLanguage<'a, C> {
|
|||
let table = &self.build_fn_table.core;
|
||||
table.build_method(self, build_ctx, property, function)
|
||||
}
|
||||
GenericTemplatePropertyKind::Self_ => {
|
||||
GenericTemplatePropertyKind::Self_(property) => {
|
||||
let table = &self.build_fn_table.keywords;
|
||||
let build = template_parser::lookup_method("Self", table, function)?;
|
||||
// For simplicity, only 0-ary method is supported.
|
||||
template_parser::expect_no_arguments(function)?;
|
||||
build()
|
||||
build(property)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: Clone> GenericTemplateLanguage<'a, C> {
|
||||
pub fn wrap_self(
|
||||
property: impl TemplateProperty<C, Output = C> + 'a,
|
||||
) -> GenericTemplatePropertyKind<'a, C> {
|
||||
GenericTemplatePropertyKind::Self_(Box::new(property))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum GenericTemplatePropertyKind<'a, C> {
|
||||
Core(CoreTemplatePropertyKind<'a, C>),
|
||||
Self_,
|
||||
Self_(Box<dyn TemplateProperty<C, Output = C> + 'a>),
|
||||
}
|
||||
|
||||
impl<'a, C: 'a> IntoTemplateProperty<'a, C> for GenericTemplatePropertyKind<'a, C> {
|
||||
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<C, Output = bool> + 'a>> {
|
||||
match self {
|
||||
GenericTemplatePropertyKind::Core(property) => property.try_into_boolean(),
|
||||
GenericTemplatePropertyKind::Self_ => None,
|
||||
GenericTemplatePropertyKind::Self_(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<C, Output = i64> + 'a>> {
|
||||
match self {
|
||||
GenericTemplatePropertyKind::Core(property) => property.try_into_integer(),
|
||||
GenericTemplatePropertyKind::Self_ => None,
|
||||
GenericTemplatePropertyKind::Self_(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<C, Output = String> + 'a>> {
|
||||
match self {
|
||||
GenericTemplatePropertyKind::Core(property) => property.try_into_plain_text(),
|
||||
GenericTemplatePropertyKind::Self_ => None,
|
||||
GenericTemplatePropertyKind::Self_(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_into_template(self) -> Option<Box<dyn Template<C> + 'a>> {
|
||||
match self {
|
||||
GenericTemplatePropertyKind::Core(property) => property.try_into_template(),
|
||||
GenericTemplatePropertyKind::Self_ => None,
|
||||
GenericTemplatePropertyKind::Self_(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -154,15 +163,19 @@ impl<'a, C: 'a> IntoTemplateProperty<'a, C> for GenericTemplatePropertyKind<'a,
|
|||
///
|
||||
/// Because the `GenericTemplateLanguage` doesn't provide a way to pass around
|
||||
/// global resources, the keyword function is allowed to capture resources.
|
||||
pub type GenericTemplateBuildKeywordFn<'a, C> =
|
||||
Box<dyn Fn() -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>> + 'a>;
|
||||
pub type GenericTemplateBuildKeywordFn<'a, C> = Box<
|
||||
dyn Fn(
|
||||
Box<dyn TemplateProperty<C, Output = C> + 'a>,
|
||||
) -> TemplateParseResult<GenericTemplatePropertyKind<'a, C>>
|
||||
+ 'a,
|
||||
>;
|
||||
|
||||
/// Table of functions that translate keyword node.
|
||||
pub type GenericTemplateBuildKeywordFnMap<'a, C> =
|
||||
HashMap<&'static str, GenericTemplateBuildKeywordFn<'a, C>>;
|
||||
|
||||
/// Symbol table of methods available in the general-purpose template.
|
||||
struct GenericTemplateBuildFnTable<'a, C: 'a> {
|
||||
struct GenericTemplateBuildFnTable<'a, C: Clone + 'a> {
|
||||
core: CoreTemplateBuildFnTable<'a, GenericTemplateLanguage<'a, C>>,
|
||||
keywords: GenericTemplateBuildKeywordFnMap<'a, C>,
|
||||
}
|
||||
|
|
|
@ -1206,7 +1206,7 @@ mod tests {
|
|||
where
|
||||
F: Fn() -> TestTemplatePropertyKind + 'static,
|
||||
{
|
||||
self.language.add_keyword(name, move || Ok(build()));
|
||||
self.language.add_keyword(name, move |_| Ok(build()));
|
||||
}
|
||||
|
||||
fn add_alias(&mut self, decl: impl AsRef<str>, defn: impl Into<String>) {
|
||||
|
|
Loading…
Reference in a new issue