3750 lines · 152.5 KB · Rust
RawDownload
// Copyright 2020-2023 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. use std::cmp::Ordering;use std::collections::HashMap;use std::io;use std::iter; use itertools::Itertools as _;use jj_lib::backend::Signature;use jj_lib::backend::Timestamp;use jj_lib::config::ConfigNamePathBuf;use jj_lib::config::ConfigValue;use jj_lib::content_hash::blake2b_hash;use jj_lib::hex_util;use jj_lib::op_store::TimestampRange;use jj_lib::settings::UserSettings;use jj_lib::time_util::DatePattern;use serde::Deserialize;use serde::de::IntoDeserializer as _; use crate::config;use crate::formatter::FormatRecorder;use crate::formatter::Formatter;use crate::template_parser;use crate::template_parser::BinaryOp;use crate::template_parser::ExpressionKind;use crate::template_parser::ExpressionNode;use crate::template_parser::FunctionCallNode;use crate::template_parser::LambdaNode;use crate::template_parser::TemplateAliasesMap;use crate::template_parser::TemplateDiagnostics;use crate::template_parser::TemplateParseError;use crate::template_parser::TemplateParseErrorKind;use crate::template_parser::TemplateParseResult;use crate::template_parser::UnaryOp;use crate::templater::BoxedSerializeProperty;use crate::templater::BoxedTemplateProperty;use crate::templater::CoalesceTemplate;use crate::templater::ConcatTemplate;use crate::templater::ConditionalTemplate;use crate::templater::Email;use crate::templater::LabelTemplate;use crate::templater::ListPropertyTemplate;use crate::templater::ListTemplate;use crate::templater::Literal;use crate::templater::PlainTextFormattedProperty;use crate::templater::PropertyPlaceholder;use crate::templater::RawEscapeSequenceTemplate;use crate::templater::ReformatTemplate;use crate::templater::SeparateTemplate;use crate::templater::SizeHint;use crate::templater::Template;use crate::templater::TemplateProperty;use crate::templater::TemplatePropertyError;use crate::templater::TemplatePropertyExt as _;use crate::templater::TemplateRenderer;use crate::templater::WrapTemplateProperty;use crate::text_util;use crate::time_util; /// Callbacks to build usage-context-specific evaluation objects from AST nodes.////// This is used to implement different meanings of `self` or different/// globally available functions in the template language depending on the/// context in which it is invoked.pub trait TemplateLanguage<'a> {    type Property: CoreTemplatePropertyVar<'a>;     fn settings(&self) -> &UserSettings;     /// Translates the given global `function` call to a property.    ///    /// This should be delegated to    /// `CoreTemplateBuildFnTable::build_function()`.    fn build_function(        &self,        diagnostics: &mut TemplateDiagnostics,        build_ctx: &BuildContext<Self::Property>,        function: &FunctionCallNode,    ) -> TemplateParseResult<Self::Property>;     /// Creates a method call thunk for the given `function` of the given    /// `property`.    fn build_method(        &self,        diagnostics: &mut TemplateDiagnostics,        build_ctx: &BuildContext<Self::Property>,        property: Self::Property,        function: &FunctionCallNode,    ) -> TemplateParseResult<Self::Property>;} /// Implements [`WrapTemplateProperty<'a, O>`] for property types.////// - `impl_property_wrappers!(Kind { Foo(Foo), FooList(Vec<Foo>), .. });` to///   implement conversion from types `Foo`, `Vec<Foo>`, .../// - `impl_property_wrappers!(<'a> Kind<'a> { .. });` for types with lifetime./// - `impl_property_wrappers!(Kind => Core { .. });` to forward conversion to///   `Kind::Core(_)`.macro_rules! impl_property_wrappers {    ($kind:path $(=> $var:ident)? { $($body:tt)* }) => {        $crate::template_builder::_impl_property_wrappers_many!(            [], 'static, $kind $(=> $var)?, { $($body)* });    };    // capture the first lifetime as the lifetime of template objects.    (<$a:lifetime $(, $p:lifetime)* $(, $q:ident)*>     $kind:path $(=> $var:ident)? { $($body:tt)* }) => {        $crate::template_builder::_impl_property_wrappers_many!(            [$a, $($p,)* $($q,)*], $a, $kind $(=> $var)?, { $($body)* });    };} macro_rules! _impl_property_wrappers_many {    // lifetime/type parameters are packed in order to disable zipping.    // https://github.com/rust-lang/rust/issues/96184#issuecomment-1294999418    ($ps:tt, $a:lifetime, $kind:path, { $( $var:ident($ty:ty), )* }) => {        $(            $crate::template_builder::_impl_property_wrappers_one!(                $ps, $a, $kind, $var, $ty, std::convert::identity);        )*    };    // variant part in body is ignored so the same body can be reused for    // implementing forwarding conversion.    ($ps:tt, $a:lifetime, $kind:path => $var:ident, { $( $ignored_var:ident($ty:ty), )* }) => {        $(            $crate::template_builder::_impl_property_wrappers_one!(                $ps, $a, $kind, $var, $ty, $crate::templater::WrapTemplateProperty::wrap_property);        )*    };} macro_rules! _impl_property_wrappers_one {    ([$($p:tt)*], $a:lifetime, $kind:path, $var:ident, $ty:ty, $inner:path) => {        impl<$($p)*> $crate::templater::WrapTemplateProperty<$a, $ty> for $kind {            fn wrap_property(property: $crate::templater::BoxedTemplateProperty<$a, $ty>) -> Self {                Self::$var($inner(property))            }        }    };} pub(crate) use _impl_property_wrappers_many;pub(crate) use _impl_property_wrappers_one;pub(crate) use impl_property_wrappers; /// Wrapper for the core template property types.pub trait CoreTemplatePropertyVar<'a>where    Self: WrapTemplateProperty<'a, String>,    Self: WrapTemplateProperty<'a, Vec<String>>,    Self: WrapTemplateProperty<'a, bool>,    Self: WrapTemplateProperty<'a, i64>,    Self: WrapTemplateProperty<'a, Option<i64>>,    Self: WrapTemplateProperty<'a, ConfigValue>,    Self: WrapTemplateProperty<'a, Signature>,    Self: WrapTemplateProperty<'a, Email>,    Self: WrapTemplateProperty<'a, SizeHint>,    Self: WrapTemplateProperty<'a, Timestamp>,    Self: WrapTemplateProperty<'a, TimestampRange>,{    fn wrap_template(template: Box<dyn Template + 'a>) -> Self;    fn wrap_list_template(template: Box<dyn ListTemplate + 'a>) -> Self;     /// Type name of the property output.    fn type_name(&self) -> &'static str;     fn try_into_boolean(self) -> Option<BoxedTemplateProperty<'a, bool>>;    fn try_into_integer(self) -> Option<BoxedTemplateProperty<'a, i64>>;     /// Transforms into a string property by formatting the value if needed.    fn try_into_stringify(self) -> Option<BoxedTemplateProperty<'a, String>>;    fn try_into_serialize(self) -> Option<BoxedSerializeProperty<'a>>;    fn try_into_template(self) -> Option<Box<dyn Template + 'a>>;     /// Transforms into a property that will evaluate to `self == other`.    fn try_into_eq(self, other: Self) -> Option<BoxedTemplateProperty<'a, bool>>;     /// Transforms into a property that will evaluate to an [`Ordering`].    fn try_into_cmp(self, other: Self) -> Option<BoxedTemplateProperty<'a, Ordering>>;} pub enum CoreTemplatePropertyKind<'a> {    String(BoxedTemplateProperty<'a, String>),    StringList(BoxedTemplateProperty<'a, Vec<String>>),    Boolean(BoxedTemplateProperty<'a, bool>),    Integer(BoxedTemplateProperty<'a, i64>),    IntegerOpt(BoxedTemplateProperty<'a, Option<i64>>),    ConfigValue(BoxedTemplateProperty<'a, ConfigValue>),    Signature(BoxedTemplateProperty<'a, Signature>),    Email(BoxedTemplateProperty<'a, Email>),    SizeHint(BoxedTemplateProperty<'a, SizeHint>),    Timestamp(BoxedTemplateProperty<'a, Timestamp>),    TimestampRange(BoxedTemplateProperty<'a, TimestampRange>),     // Both TemplateProperty and Template can represent a value to be evaluated    // dynamically, which suggests that `Box<dyn Template + 'a>` could be    // composed as `Box<dyn TemplateProperty<Output = Box<dyn Template ..`.    // However, there's a subtle difference: TemplateProperty is strict on    // error, whereas Template is usually lax and prints an error inline. If    // `concat(x, y)` were a property returning Template, and if `y` failed to    // evaluate, the whole expression would fail. In this example, a partial    // evaluation output is more useful. That's one reason why Template isn't    // wrapped in a TemplateProperty. Another reason is that the outermost    // caller expects a Template, not a TemplateProperty of Template output.    Template(Box<dyn Template + 'a>),    ListTemplate(Box<dyn ListTemplate + 'a>),} /// Implements `WrapTemplateProperty<type>` for core property types.////// Use `impl_core_property_wrappers!(<'a> Kind<'a> => Core);` to implement/// forwarding conversion.macro_rules! impl_core_property_wrappers {    ($($head:tt)+) => {        $crate::template_builder::impl_property_wrappers!($($head)+ {            String(String),            StringList(Vec<String>),            Boolean(bool),            Integer(i64),            IntegerOpt(Option<i64>),            ConfigValue(jj_lib::config::ConfigValue),            Signature(jj_lib::backend::Signature),            Email($crate::templater::Email),            SizeHint($crate::templater::SizeHint),            Timestamp(jj_lib::backend::Timestamp),            TimestampRange(jj_lib::op_store::TimestampRange),        });    };} pub(crate) use impl_core_property_wrappers; impl_core_property_wrappers!(<'a> CoreTemplatePropertyKind<'a>); impl<'a> CoreTemplatePropertyVar<'a> for CoreTemplatePropertyKind<'a> {    fn wrap_template(template: Box<dyn Template + 'a>) -> Self {        Self::Template(template)    }     fn wrap_list_template(template: Box<dyn ListTemplate + 'a>) -> Self {        Self::ListTemplate(template)    }     fn type_name(&self) -> &'static str {        match self {            Self::String(_) => "String",            Self::StringList(_) => "List<String>",            Self::Boolean(_) => "Boolean",            Self::Integer(_) => "Integer",            Self::IntegerOpt(_) => "Option<Integer>",            Self::ConfigValue(_) => "ConfigValue",            Self::Signature(_) => "Signature",            Self::Email(_) => "Email",            Self::SizeHint(_) => "SizeHint",            Self::Timestamp(_) => "Timestamp",            Self::TimestampRange(_) => "TimestampRange",            Self::Template(_) => "Template",            Self::ListTemplate(_) => "ListTemplate",        }    }     fn try_into_boolean(self) -> Option<BoxedTemplateProperty<'a, bool>> {        match self {            Self::String(property) => Some(property.map(|s| !s.is_empty()).into_dyn()),            Self::StringList(property) => Some(property.map(|l| !l.is_empty()).into_dyn()),            Self::Boolean(property) => Some(property),            Self::Integer(_) => None,            Self::IntegerOpt(property) => Some(property.map(|opt| opt.is_some()).into_dyn()),            Self::ConfigValue(_) => None,            Self::Signature(_) => None,            Self::Email(property) => Some(property.map(|e| !e.0.is_empty()).into_dyn()),            Self::SizeHint(_) => None,            Self::Timestamp(_) => None,            Self::TimestampRange(_) => None,            // Template types could also be evaluated to boolean, but it's less likely            // to apply label() or .map() and use the result as conditional. It's also            // unclear whether ListTemplate should behave as a "list" or a "template".            Self::Template(_) => None,            Self::ListTemplate(_) => None,        }    }     fn try_into_integer(self) -> Option<BoxedTemplateProperty<'a, i64>> {        match self {            Self::Integer(property) => Some(property),            Self::IntegerOpt(property) => Some(property.try_unwrap("Integer").into_dyn()),            _ => None,        }    }     fn try_into_stringify(self) -> Option<BoxedTemplateProperty<'a, String>> {        match self {            Self::String(property) => Some(property),            _ => {                let template = self.try_into_template()?;                Some(PlainTextFormattedProperty::new(template).into_dyn())            }        }    }     fn try_into_serialize(self) -> Option<BoxedSerializeProperty<'a>> {        match self {            Self::String(property) => Some(property.into_serialize()),            Self::StringList(property) => Some(property.into_serialize()),            Self::Boolean(property) => Some(property.into_serialize()),            Self::Integer(property) => Some(property.into_serialize()),            Self::IntegerOpt(property) => Some(property.into_serialize()),            Self::ConfigValue(property) => {                Some(property.map(config::to_serializable_value).into_serialize())            }            Self::Signature(property) => Some(property.into_serialize()),            Self::Email(property) => Some(property.into_serialize()),            Self::SizeHint(property) => Some(property.into_serialize()),            Self::Timestamp(property) => Some(property.into_serialize()),            Self::TimestampRange(property) => Some(property.into_serialize()),            Self::Template(_) => None,            Self::ListTemplate(_) => None,        }    }     fn try_into_template(self) -> Option<Box<dyn Template + 'a>> {        match self {            Self::String(property) => Some(property.into_template()),            Self::StringList(property) => Some(property.into_template()),            Self::Boolean(property) => Some(property.into_template()),            Self::Integer(property) => Some(property.into_template()),            Self::IntegerOpt(property) => Some(property.into_template()),            Self::ConfigValue(property) => Some(property.into_template()),            Self::Signature(property) => Some(property.into_template()),            Self::Email(property) => Some(property.into_template()),            Self::SizeHint(_) => None,            Self::Timestamp(property) => Some(property.into_template()),            Self::TimestampRange(property) => Some(property.into_template()),            Self::Template(template) => Some(template),            Self::ListTemplate(template) => Some(template.into_template()),        }    }     fn try_into_eq(self, other: Self) -> Option<BoxedTemplateProperty<'a, bool>> {        match (self, other) {            (Self::String(lhs), Self::String(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l == r).into_dyn())            }            (Self::String(lhs), Self::Email(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l == r.0).into_dyn())            }            (Self::Boolean(lhs), Self::Boolean(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l == r).into_dyn())            }            (Self::Integer(lhs), Self::Integer(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l == r).into_dyn())            }            (Self::Integer(lhs), Self::IntegerOpt(rhs)) => {                Some((lhs, rhs).map(|(l, r)| Some(l) == r).into_dyn())            }            (Self::IntegerOpt(lhs), Self::Integer(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l == Some(r)).into_dyn())            }            (Self::IntegerOpt(lhs), Self::IntegerOpt(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l == r).into_dyn())            }            (Self::Email(lhs), Self::Email(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l == r).into_dyn())            }            (Self::Email(lhs), Self::String(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l.0 == r).into_dyn())            }            (Self::String(_), _) => None,            (Self::StringList(_), _) => None,            (Self::Boolean(_), _) => None,            (Self::Integer(_), _) => None,            (Self::IntegerOpt(_), _) => None,            (Self::ConfigValue(_), _) => None,            (Self::Signature(_), _) => None,            (Self::Email(_), _) => None,            (Self::SizeHint(_), _) => None,            (Self::Timestamp(_), _) => None,            (Self::TimestampRange(_), _) => None,            (Self::Template(_), _) => None,            (Self::ListTemplate(_), _) => None,        }    }     fn try_into_cmp(self, other: Self) -> Option<BoxedTemplateProperty<'a, Ordering>> {        match (self, other) {            (Self::Integer(lhs), Self::Integer(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l.cmp(&r)).into_dyn())            }            (Self::Integer(lhs), Self::IntegerOpt(rhs)) => {                Some((lhs, rhs).map(|(l, r)| Some(l).cmp(&r)).into_dyn())            }            (Self::IntegerOpt(lhs), Self::Integer(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l.cmp(&Some(r))).into_dyn())            }            (Self::IntegerOpt(lhs), Self::IntegerOpt(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l.cmp(&r)).into_dyn())            }            (Self::String(_), _) => None,            (Self::StringList(_), _) => None,            (Self::Boolean(_), _) => None,            (Self::Integer(_), _) => None,            (Self::IntegerOpt(_), _) => None,            (Self::ConfigValue(_), _) => None,            (Self::Signature(_), _) => None,            (Self::Email(_), _) => None,            (Self::SizeHint(_), _) => None,            (Self::Timestamp(_), _) => None,            (Self::TimestampRange(_), _) => None,            (Self::Template(_), _) => None,            (Self::ListTemplate(_), _) => None,        }    }} /// Function that translates global function call node.// The lifetime parameter 'a could be replaced with for<'a> to keep the method// table away from a certain lifetime. That's technically more correct, but I// couldn't find an easy way to expand that to the core template methods, which// are defined for L: TemplateLanguage<'a>. That's why the build fn table is// bound to a named lifetime, and therefore can't be cached statically.pub type TemplateBuildFunctionFn<'a, L, P> =    fn(&L, &mut TemplateDiagnostics, &BuildContext<P>, &FunctionCallNode) -> TemplateParseResult<P>; type BuildMethodFn<'a, L, T, P> = fn(    &L,    &mut TemplateDiagnostics,    &BuildContext<P>,    T,    &FunctionCallNode,) -> TemplateParseResult<P>; /// Function that translates method call node of self type `T`.pub type TemplateBuildMethodFn<'a, L, T, P> = BuildMethodFn<'a, L, BoxedTemplateProperty<'a, T>, P>; /// Function that translates method call node of `Template`.pub type BuildTemplateMethodFn<'a, L, P> = BuildMethodFn<'a, L, Box<dyn Template + 'a>, P>; /// Function that translates method call node of `ListTemplate`.pub type BuildListTemplateMethodFn<'a, L, P> = BuildMethodFn<'a, L, Box<dyn ListTemplate + 'a>, P>; /// Table of functions that translate global function call node.pub type TemplateBuildFunctionFnMap<'a, L, P = <L as TemplateLanguage<'a>>::Property> =    HashMap<&'static str, TemplateBuildFunctionFn<'a, L, P>>; /// Table of functions that translate method call node of self type `T`.pub type TemplateBuildMethodFnMap<'a, L, T, P = <L as TemplateLanguage<'a>>::Property> =    HashMap<&'static str, TemplateBuildMethodFn<'a, L, T, P>>; /// Table of functions that translate method call node of `Template`.pub type BuildTemplateMethodFnMap<'a, L, P = <L as TemplateLanguage<'a>>::Property> =    HashMap<&'static str, BuildTemplateMethodFn<'a, L, P>>; /// Table of functions that translate method call node of `ListTemplate`.pub type BuildListTemplateMethodFnMap<'a, L, P = <L as TemplateLanguage<'a>>::Property> =    HashMap<&'static str, BuildListTemplateMethodFn<'a, L, P>>; /// Symbol table of functions and methods available in the core template.pub struct CoreTemplateBuildFnTable<'a, L: ?Sized, P = <L as TemplateLanguage<'a>>::Property> {    pub functions: TemplateBuildFunctionFnMap<'a, L, P>,    pub string_methods: TemplateBuildMethodFnMap<'a, L, String, P>,    pub string_list_methods: TemplateBuildMethodFnMap<'a, L, Vec<String>, P>,    pub boolean_methods: TemplateBuildMethodFnMap<'a, L, bool, P>,    pub integer_methods: TemplateBuildMethodFnMap<'a, L, i64, P>,    pub config_value_methods: TemplateBuildMethodFnMap<'a, L, ConfigValue, P>,    pub email_methods: TemplateBuildMethodFnMap<'a, L, Email, P>,    pub signature_methods: TemplateBuildMethodFnMap<'a, L, Signature, P>,    pub size_hint_methods: TemplateBuildMethodFnMap<'a, L, SizeHint, P>,    pub timestamp_methods: TemplateBuildMethodFnMap<'a, L, Timestamp, P>,    pub timestamp_range_methods: TemplateBuildMethodFnMap<'a, L, TimestampRange, P>,    pub template_methods: BuildTemplateMethodFnMap<'a, L, P>,    pub list_template_methods: BuildListTemplateMethodFnMap<'a, L, P>,} pub fn merge_fn_map<'s, F>(base: &mut HashMap<&'s str, F>, extension: HashMap<&'s str, F>) {    for (name, function) in extension {        if base.insert(name, function).is_some() {            panic!("Conflicting template definitions for '{name}' function");        }    }} impl<L: ?Sized, P> CoreTemplateBuildFnTable<'_, L, P> {    pub fn empty() -> Self {        Self {            functions: HashMap::new(),            string_methods: HashMap::new(),            string_list_methods: HashMap::new(),            boolean_methods: HashMap::new(),

Lines 1–500 of 3750.