3320 lines · 135.2 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::dsl_util::AliasExpandError as _;use jj_lib::settings::UserSettings;use jj_lib::time_util::DatePattern;use serde::de::IntoDeserializer as _;use serde::Deserialize; 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::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::TimestampRange;use crate::text_util;use crate::time_util; /// Callbacks to build language-specific evaluation objects from AST nodes.pub trait TemplateLanguage<'a> {    type Property: IntoTemplateProperty<'a>;     fn wrap_string(property: impl TemplateProperty<Output = String> + 'a) -> Self::Property;    fn wrap_string_list(        property: impl TemplateProperty<Output = Vec<String>> + 'a,    ) -> Self::Property;    fn wrap_boolean(property: impl TemplateProperty<Output = bool> + 'a) -> Self::Property;    fn wrap_integer(property: impl TemplateProperty<Output = i64> + 'a) -> Self::Property;    fn wrap_integer_opt(        property: impl TemplateProperty<Output = Option<i64>> + 'a,    ) -> Self::Property;    fn wrap_config_value(        property: impl TemplateProperty<Output = ConfigValue> + 'a,    ) -> Self::Property;    fn wrap_signature(property: impl TemplateProperty<Output = Signature> + 'a) -> Self::Property;    fn wrap_email(property: impl TemplateProperty<Output = Email> + 'a) -> Self::Property;    fn wrap_size_hint(property: impl TemplateProperty<Output = SizeHint> + 'a) -> Self::Property;    fn wrap_timestamp(property: impl TemplateProperty<Output = Timestamp> + 'a) -> Self::Property;    fn wrap_timestamp_range(        property: impl TemplateProperty<Output = TimestampRange> + 'a,    ) -> Self::Property;     fn wrap_template(template: Box<dyn Template + 'a>) -> Self::Property;    fn wrap_list_template(template: Box<dyn ListTemplate + 'a>) -> Self::Property;     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>;     fn build_method(        &self,        diagnostics: &mut TemplateDiagnostics,        build_ctx: &BuildContext<Self::Property>,        property: Self::Property,        function: &FunctionCallNode,    ) -> TemplateParseResult<Self::Property>;} /// Implements `TemplateLanguage::wrap_<type>()` functions.////// - `impl_core_wrap_property_fns('a)` for `CoreTemplatePropertyKind`,/// - `impl_core_wrap_property_fns('a, MyKind::Core)` for `MyKind::Core(..)`.macro_rules! impl_core_wrap_property_fns {    ($a:lifetime) => {        $crate::template_builder::impl_core_wrap_property_fns!($a, std::convert::identity);    };    ($a:lifetime, $outer:path) => {        $crate::template_builder::impl_wrap_property_fns!(            $a, $crate::template_builder::CoreTemplatePropertyKind, $outer, {                wrap_string(String) => String,                wrap_string_list(Vec<String>) => StringList,                wrap_boolean(bool) => Boolean,                wrap_integer(i64) => Integer,                wrap_integer_opt(Option<i64>) => IntegerOpt,                wrap_config_value(jj_lib::config::ConfigValue) => ConfigValue,                wrap_signature(jj_lib::backend::Signature) => Signature,                wrap_email($crate::templater::Email) => Email,                wrap_size_hint($crate::templater::SizeHint) => SizeHint,                wrap_timestamp(jj_lib::backend::Timestamp) => Timestamp,                wrap_timestamp_range($crate::templater::TimestampRange) => TimestampRange,            }        );        fn wrap_template(            template: Box<dyn $crate::templater::Template + $a>,        ) -> Self::Property {            use $crate::template_builder::CoreTemplatePropertyKind as Kind;            $outer(Kind::Template(template))        }        fn wrap_list_template(            template: Box<dyn $crate::templater::ListTemplate + $a>,        ) -> Self::Property {            use $crate::template_builder::CoreTemplatePropertyKind as Kind;            $outer(Kind::ListTemplate(template))        }    };} macro_rules! impl_wrap_property_fns {    ($a:lifetime, $kind:path, $outer:path, { $( $func:ident($ty:ty) => $var:ident, )+ }) => {        $(            fn $func(                property: impl $crate::templater::TemplateProperty<Output = $ty> + $a,            ) -> Self::Property {                use $kind as Kind; // https://github.com/rust-lang/rust/issues/48067                $outer(Kind::$var(Box::new(property)))            }        )+    };} pub(crate) use impl_core_wrap_property_fns;pub(crate) use impl_wrap_property_fns; /// Provides access to basic template property types.pub trait IntoTemplateProperty<'a> {    /// Type name of the property output.    fn type_name(&self) -> &'static str;     fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<Output = bool> + 'a>>;    fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<Output = i64> + 'a>>;     fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<Output = String> + '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<Box<dyn TemplateProperty<Output = bool> + 'a>>;     /// Transforms into a property that will evaluate to an [`Ordering`].    fn try_into_cmp(self, other: Self)        -> Option<Box<dyn TemplateProperty<Output = Ordering> + 'a>>;} pub enum CoreTemplatePropertyKind<'a> {    String(Box<dyn TemplateProperty<Output = String> + 'a>),    StringList(Box<dyn TemplateProperty<Output = Vec<String>> + 'a>),    Boolean(Box<dyn TemplateProperty<Output = bool> + 'a>),    Integer(Box<dyn TemplateProperty<Output = i64> + 'a>),    IntegerOpt(Box<dyn TemplateProperty<Output = Option<i64>> + 'a>),    ConfigValue(Box<dyn TemplateProperty<Output = ConfigValue> + 'a>),    Signature(Box<dyn TemplateProperty<Output = Signature> + 'a>),    Email(Box<dyn TemplateProperty<Output = Email> + 'a>),    SizeHint(Box<dyn TemplateProperty<Output = SizeHint> + 'a>),    Timestamp(Box<dyn TemplateProperty<Output = Timestamp> + 'a>),    TimestampRange(Box<dyn TemplateProperty<Output = TimestampRange> + 'a>),     // 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>),} impl<'a> IntoTemplateProperty<'a> for CoreTemplatePropertyKind<'a> {    fn type_name(&self) -> &'static str {        match self {            CoreTemplatePropertyKind::String(_) => "String",            CoreTemplatePropertyKind::StringList(_) => "List<String>",            CoreTemplatePropertyKind::Boolean(_) => "Boolean",            CoreTemplatePropertyKind::Integer(_) => "Integer",            CoreTemplatePropertyKind::IntegerOpt(_) => "Option<Integer>",            CoreTemplatePropertyKind::ConfigValue(_) => "ConfigValue",            CoreTemplatePropertyKind::Signature(_) => "Signature",            CoreTemplatePropertyKind::Email(_) => "Email",            CoreTemplatePropertyKind::SizeHint(_) => "SizeHint",            CoreTemplatePropertyKind::Timestamp(_) => "Timestamp",            CoreTemplatePropertyKind::TimestampRange(_) => "TimestampRange",            CoreTemplatePropertyKind::Template(_) => "Template",            CoreTemplatePropertyKind::ListTemplate(_) => "ListTemplate",        }    }     fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<Output = bool> + 'a>> {        match self {            CoreTemplatePropertyKind::String(property) => {                Some(Box::new(property.map(|s| !s.is_empty())))            }            CoreTemplatePropertyKind::StringList(property) => {                Some(Box::new(property.map(|l| !l.is_empty())))            }            CoreTemplatePropertyKind::Boolean(property) => Some(property),            CoreTemplatePropertyKind::Integer(_) => None,            CoreTemplatePropertyKind::IntegerOpt(property) => {                Some(Box::new(property.map(|opt| opt.is_some())))            }            CoreTemplatePropertyKind::ConfigValue(_) => None,            CoreTemplatePropertyKind::Signature(_) => None,            CoreTemplatePropertyKind::Email(property) => {                Some(Box::new(property.map(|e| !e.0.is_empty())))            }            CoreTemplatePropertyKind::SizeHint(_) => None,            CoreTemplatePropertyKind::Timestamp(_) => None,            CoreTemplatePropertyKind::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".            CoreTemplatePropertyKind::Template(_) => None,            CoreTemplatePropertyKind::ListTemplate(_) => None,        }    }     fn try_into_integer(self) -> Option<Box<dyn TemplateProperty<Output = i64> + 'a>> {        match self {            CoreTemplatePropertyKind::Integer(property) => Some(property),            CoreTemplatePropertyKind::IntegerOpt(property) => {                Some(Box::new(property.try_unwrap("Integer")))            }            _ => None,        }    }     fn try_into_plain_text(self) -> Option<Box<dyn TemplateProperty<Output = String> + 'a>> {        match self {            CoreTemplatePropertyKind::String(property) => Some(property),            _ => {                let template = self.try_into_template()?;                Some(Box::new(PlainTextFormattedProperty::new(template)))            }        }    }     fn try_into_template(self) -> Option<Box<dyn Template + 'a>> {        match self {            CoreTemplatePropertyKind::String(property) => Some(property.into_template()),            CoreTemplatePropertyKind::StringList(property) => Some(property.into_template()),            CoreTemplatePropertyKind::Boolean(property) => Some(property.into_template()),            CoreTemplatePropertyKind::Integer(property) => Some(property.into_template()),            CoreTemplatePropertyKind::IntegerOpt(property) => Some(property.into_template()),            CoreTemplatePropertyKind::ConfigValue(property) => Some(property.into_template()),            CoreTemplatePropertyKind::Signature(property) => Some(property.into_template()),            CoreTemplatePropertyKind::Email(property) => Some(property.into_template()),            CoreTemplatePropertyKind::SizeHint(_) => None,            CoreTemplatePropertyKind::Timestamp(property) => Some(property.into_template()),            CoreTemplatePropertyKind::TimestampRange(property) => Some(property.into_template()),            CoreTemplatePropertyKind::Template(template) => Some(template),            CoreTemplatePropertyKind::ListTemplate(template) => Some(template.into_template()),        }    }     fn try_into_eq(self, other: Self) -> Option<Box<dyn TemplateProperty<Output = bool> + 'a>> {        match (self, other) {            (CoreTemplatePropertyKind::String(lhs), CoreTemplatePropertyKind::String(rhs)) => {                Some(Box::new((lhs, rhs).map(|(l, r)| l == r)))            }            (CoreTemplatePropertyKind::String(lhs), CoreTemplatePropertyKind::Email(rhs)) => {                Some(Box::new((lhs, rhs).map(|(l, r)| l == r.0)))            }            (CoreTemplatePropertyKind::Boolean(lhs), CoreTemplatePropertyKind::Boolean(rhs)) => {                Some(Box::new((lhs, rhs).map(|(l, r)| l == r)))            }            (CoreTemplatePropertyKind::Integer(lhs), CoreTemplatePropertyKind::Integer(rhs)) => {                Some(Box::new((lhs, rhs).map(|(l, r)| l == r)))            }            (CoreTemplatePropertyKind::Email(lhs), CoreTemplatePropertyKind::Email(rhs)) => {                Some(Box::new((lhs, rhs).map(|(l, r)| l == r)))            }            (CoreTemplatePropertyKind::Email(lhs), CoreTemplatePropertyKind::String(rhs)) => {                Some(Box::new((lhs, rhs).map(|(l, r)| l.0 == r)))            }            (CoreTemplatePropertyKind::String(_), _) => None,            (CoreTemplatePropertyKind::StringList(_), _) => None,            (CoreTemplatePropertyKind::Boolean(_), _) => None,            (CoreTemplatePropertyKind::Integer(_), _) => None,            (CoreTemplatePropertyKind::IntegerOpt(_), _) => None,            (CoreTemplatePropertyKind::ConfigValue(_), _) => None,            (CoreTemplatePropertyKind::Signature(_), _) => None,            (CoreTemplatePropertyKind::Email(_), _) => None,            (CoreTemplatePropertyKind::SizeHint(_), _) => None,            (CoreTemplatePropertyKind::Timestamp(_), _) => None,            (CoreTemplatePropertyKind::TimestampRange(_), _) => None,            (CoreTemplatePropertyKind::Template(_), _) => None,            (CoreTemplatePropertyKind::ListTemplate(_), _) => None,        }    }     fn try_into_cmp(        self,        other: Self,    ) -> Option<Box<dyn TemplateProperty<Output = Ordering> + 'a>> {        match (self, other) {            (CoreTemplatePropertyKind::Integer(lhs), CoreTemplatePropertyKind::Integer(rhs)) => {                Some(Box::new((lhs, rhs).map(|(l, r)| l.cmp(&r))))            }            (CoreTemplatePropertyKind::String(_), _) => None,            (CoreTemplatePropertyKind::StringList(_), _) => None,            (CoreTemplatePropertyKind::Boolean(_), _) => None,            (CoreTemplatePropertyKind::Integer(_), _) => None,            (CoreTemplatePropertyKind::IntegerOpt(_), _) => None,            (CoreTemplatePropertyKind::ConfigValue(_), _) => None,            (CoreTemplatePropertyKind::Signature(_), _) => None,            (CoreTemplatePropertyKind::Email(_), _) => None,            (CoreTemplatePropertyKind::SizeHint(_), _) => None,            (CoreTemplatePropertyKind::Timestamp(_), _) => None,            (CoreTemplatePropertyKind::TimestampRange(_), _) => None,            (CoreTemplatePropertyKind::Template(_), _) => None,            (CoreTemplatePropertyKind::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> =    fn(        &L,        &mut TemplateDiagnostics,        &BuildContext<<L as TemplateLanguage<'a>>::Property>,        &FunctionCallNode,    ) -> TemplateParseResult<<L as TemplateLanguage<'a>>::Property>; /// Function that translates method call node of self type `T`.pub type TemplateBuildMethodFn<'a, L, T> =    fn(        &L,        &mut TemplateDiagnostics,        &BuildContext<<L as TemplateLanguage<'a>>::Property>,        Box<dyn TemplateProperty<Output = T> + 'a>,        &FunctionCallNode,    ) -> TemplateParseResult<<L as TemplateLanguage<'a>>::Property>; /// Table of functions that translate global function call node.pub type TemplateBuildFunctionFnMap<'a, L> = HashMap<&'static str, TemplateBuildFunctionFn<'a, L>>; /// Table of functions that translate method call node of self type `T`.pub type TemplateBuildMethodFnMap<'a, L, T> =    HashMap<&'static str, TemplateBuildMethodFn<'a, L, T>>; /// Symbol table of functions and methods available in the core template.pub struct CoreTemplateBuildFnTable<'a, L: TemplateLanguage<'a> + ?Sized> {    pub functions: TemplateBuildFunctionFnMap<'a, L>,    pub string_methods: TemplateBuildMethodFnMap<'a, L, String>,    pub boolean_methods: TemplateBuildMethodFnMap<'a, L, bool>,    pub integer_methods: TemplateBuildMethodFnMap<'a, L, i64>,    pub config_value_methods: TemplateBuildMethodFnMap<'a, L, ConfigValue>,    pub email_methods: TemplateBuildMethodFnMap<'a, L, Email>,    pub signature_methods: TemplateBuildMethodFnMap<'a, L, Signature>,    pub size_hint_methods: TemplateBuildMethodFnMap<'a, L, SizeHint>,    pub timestamp_methods: TemplateBuildMethodFnMap<'a, L, Timestamp>,    pub timestamp_range_methods: TemplateBuildMethodFnMap<'a, L, TimestampRange>,} 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<'a, L: TemplateLanguage<'a> + ?Sized> CoreTemplateBuildFnTable<'a, L> {    /// Creates new symbol table containing the builtin functions and methods.    pub fn builtin() -> Self {        CoreTemplateBuildFnTable {            functions: builtin_functions(),            string_methods: builtin_string_methods(),            boolean_methods: HashMap::new(),            integer_methods: HashMap::new(),            config_value_methods: builtin_config_value_methods(),            signature_methods: builtin_signature_methods(),            email_methods: builtin_email_methods(),            size_hint_methods: builtin_size_hint_methods(),            timestamp_methods: builtin_timestamp_methods(),            timestamp_range_methods: builtin_timestamp_range_methods(),        }    }     pub fn empty() -> Self {        CoreTemplateBuildFnTable {            functions: HashMap::new(),            string_methods: HashMap::new(),            boolean_methods: HashMap::new(),            integer_methods: HashMap::new(),            config_value_methods: HashMap::new(),            signature_methods: HashMap::new(),            email_methods: HashMap::new(),            size_hint_methods: HashMap::new(),            timestamp_methods: HashMap::new(),            timestamp_range_methods: HashMap::new(),        }    }     pub fn merge(&mut self, extension: CoreTemplateBuildFnTable<'a, L>) {        let CoreTemplateBuildFnTable {            functions,            string_methods,            boolean_methods,            integer_methods,            config_value_methods,            signature_methods,            email_methods,            size_hint_methods,            timestamp_methods,            timestamp_range_methods,        } = extension;         merge_fn_map(&mut self.functions, functions);        merge_fn_map(&mut self.string_methods, string_methods);        merge_fn_map(&mut self.boolean_methods, boolean_methods);        merge_fn_map(&mut self.integer_methods, integer_methods);        merge_fn_map(&mut self.config_value_methods, config_value_methods);        merge_fn_map(&mut self.signature_methods, signature_methods);        merge_fn_map(&mut self.email_methods, email_methods);        merge_fn_map(&mut self.size_hint_methods, size_hint_methods);        merge_fn_map(&mut self.timestamp_methods, timestamp_methods);        merge_fn_map(&mut self.timestamp_range_methods, timestamp_range_methods);    }     /// Translates the function call node `function` by using this symbol table.    pub fn build_function(        &self,        language: &L,        diagnostics: &mut TemplateDiagnostics,        build_ctx: &BuildContext<L::Property>,        function: &FunctionCallNode,    ) -> TemplateParseResult<L::Property> {        let table = &self.functions;        let build = template_parser::lookup_function(table, function)?;        build(language, diagnostics, build_ctx, function)    }     /// Applies the method call node `function` to the given `property` by using    /// this symbol table.    pub fn build_method(        &self,        language: &L,        diagnostics: &mut TemplateDiagnostics,        build_ctx: &BuildContext<L::Property>,        property: CoreTemplatePropertyKind<'a>,        function: &FunctionCallNode,    ) -> TemplateParseResult<L::Property> {        let type_name = property.type_name();        match property {

Lines 1–500 of 3320.