5939 lines · 248.8 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 std::path::Path;use std::path::PathBuf; use bstr::BString;use bstr::ByteSlice as _;use itertools::Itertools as _;use jj_lib::backend::Signature;use jj_lib::backend::Timestamp;use jj_lib::config::ConfigGetResultExt as _;use jj_lib::config::ConfigNamePathBuf;use jj_lib::config::ConfigValue;use jj_lib::content_hash::blake2b_hash;use jj_lib::file_util;use jj_lib::hex_util;use jj_lib::op_store::TimestampRange;use jj_lib::settings::UserSettings;use jj_lib::str_util::StringPattern;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::AnyTemplateProperty;use crate::templater::BoxedAnyProperty;use crate::templater::BoxedSerializeProperty;use crate::templater::BoxedTemplateProperty;use crate::templater::CoalesceTemplate;use crate::templater::ConcatTemplate;use crate::templater::ConditionalProperty;use crate::templater::Email;use crate::templater::HyperlinkTemplate;use crate::templater::JoinTemplate;use crate::templater::LabelTemplate;use crate::templater::ListMapProperty;use crate::templater::ListPropertyTemplate;use crate::templater::Literal;use crate::templater::PlainTextFormattedProperty;use crate::templater::PropertyPlaceholder;use crate::templater::RawEscapeSequenceTemplate;use crate::templater::ReformatTemplate;use crate::templater::RegexCaptures;use crate::templater::SeparateTemplate;use crate::templater::SizeHint;use crate::templater::Template;use crate::templater::TemplateFormatter;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::text_util::write_replaced;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> + 'a;     fn settings(&self) -> &UserSettings;     /// Returns the working directory for filesystem path template methods.    fn current_dir(&self) -> &Path;     /// 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, BString>,    Self: WrapTemplateProperty<'a, Vec<BString>>,    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, Option<ConfigValue>>,    Self: WrapTemplateProperty<'a, PathBuf>,    Self: WrapTemplateProperty<'a, Option<PathBuf>>,    Self: WrapTemplateProperty<'a, Signature>,    Self: WrapTemplateProperty<'a, Email>,    Self: WrapTemplateProperty<'a, SizeHint>,    Self: WrapTemplateProperty<'a, RegexCaptures>,    Self: WrapTemplateProperty<'a, Timestamp>,    Self: WrapTemplateProperty<'a, TimestampRange>,{    fn wrap_template(template: Box<dyn Template + 'a>) -> Self;    fn wrap_any(property: BoxedAnyProperty<'a>) -> Self;    fn wrap_any_list(property: BoxedAnyProperty<'a>) -> Self;     /// Type name of the property output.    fn type_name(&self) -> &'static str;     /// Extracts property of `ByteString` type or newtype.    fn try_into_byte_string(self) -> Result<BoxedTemplateProperty<'a, BString>, Self>;    /// Extracts property of `String` type or newtype.    fn try_into_string(self) -> Result<BoxedTemplateProperty<'a, String>, Self>;    // TODO: rename try_into_boolean() because it isn't a pure extraction fn?    fn try_into_boolean(self) -> Result<BoxedTemplateProperty<'a, bool>, Self>;    fn try_into_integer(self) -> Result<BoxedTemplateProperty<'a, i64>, Self>;    fn try_into_timestamp(self) -> Result<BoxedTemplateProperty<'a, Timestamp>, Self>;     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> {    ByteString(BoxedTemplateProperty<'a, BString>),    ByteStringList(BoxedTemplateProperty<'a, Vec<BString>>),    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>),    ConfigValueOpt(BoxedTemplateProperty<'a, Option<ConfigValue>>),    FsPath(BoxedTemplateProperty<'a, PathBuf>),    FsPathOpt(BoxedTemplateProperty<'a, Option<PathBuf>>),    Signature(BoxedTemplateProperty<'a, Signature>),    Email(BoxedTemplateProperty<'a, Email>),    SizeHint(BoxedTemplateProperty<'a, SizeHint>),    RegexCaptures(BoxedTemplateProperty<'a, RegexCaptures>),    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>),    Any(BoxedAnyProperty<'a>),    AnyList(BoxedAnyProperty<'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)+ {            ByteString(bstr::BString),            ByteStringList(Vec<bstr::BString>),            String(String),            StringList(Vec<String>),            Boolean(bool),            Integer(i64),            IntegerOpt(Option<i64>),            ConfigValue(jj_lib::config::ConfigValue),            ConfigValueOpt(Option<jj_lib::config::ConfigValue>),            FsPath(std::path::PathBuf),            FsPathOpt(Option<std::path::PathBuf>),            Signature(jj_lib::backend::Signature),            Email($crate::templater::Email),            SizeHint($crate::templater::SizeHint),            RegexCaptures($crate::templater::RegexCaptures),            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_any(property: BoxedAnyProperty<'a>) -> Self {        Self::Any(property)    }     fn wrap_any_list(property: BoxedAnyProperty<'a>) -> Self {        Self::AnyList(property)    }     fn type_name(&self) -> &'static str {        match self {            Self::ByteString(_) => "ByteString",            Self::ByteStringList(_) => "List<ByteString>",            Self::String(_) => "String",            Self::StringList(_) => "List<String>",            Self::Boolean(_) => "Boolean",            Self::Integer(_) => "Integer",            Self::IntegerOpt(_) => "Option<Integer>",            Self::ConfigValue(_) => "ConfigValue",            Self::ConfigValueOpt(_) => "Option<ConfigValue>",            Self::FsPath(_) => "FsPath",            Self::FsPathOpt(_) => "Option<FsPath>",            Self::Signature(_) => "Signature",            Self::Email(_) => "Email",            Self::SizeHint(_) => "SizeHint",            Self::RegexCaptures(_) => "RegexCaptures",            Self::Timestamp(_) => "Timestamp",            Self::TimestampRange(_) => "TimestampRange",            Self::Template(_) => "Template",            Self::Any(_) => "Any",            Self::AnyList(_) => "AnyList",        }    }     fn try_into_byte_string(self) -> Result<BoxedTemplateProperty<'a, BString>, Self> {        match self {            Self::ByteString(property) => Ok(property),            _ => Err(self),        }    }     fn try_into_string(self) -> Result<BoxedTemplateProperty<'a, String>, Self> {        match self {            Self::String(property) => Ok(property),            _ => Err(self),        }    }     fn try_into_boolean(self) -> Result<BoxedTemplateProperty<'a, bool>, Self> {        match self {            Self::ByteString(property) => Ok(property.map(|s| !s.is_empty()).into_dyn()),            Self::ByteStringList(property) => Ok(property.map(|l| !l.is_empty()).into_dyn()),            Self::String(property) => Ok(property.map(|s| !s.is_empty()).into_dyn()),            Self::StringList(property) => Ok(property.map(|l| !l.is_empty()).into_dyn()),            Self::Boolean(property) => Ok(property),            Self::Integer(_) => Err(self),            Self::IntegerOpt(property) => Ok(property.map(|opt| opt.is_some()).into_dyn()),            Self::ConfigValue(_) => Err(self),            Self::ConfigValueOpt(property) => Ok(property.map(|opt| opt.is_some()).into_dyn()),            Self::FsPath(_) => Err(self),            Self::FsPathOpt(property) => Ok(property.map(|opt| opt.is_some()).into_dyn()),            Self::Signature(_) => Err(self),            Self::Email(property) => Ok(property.map(|e| !e.0.is_empty()).into_dyn()),            Self::SizeHint(_) => Err(self),            Self::RegexCaptures(_) => Err(self),            Self::Timestamp(_) => Err(self),            Self::TimestampRange(_) => Err(self),            // Template and AnyList types could also be evaluated to boolean,            // but it's less likely to apply label() or .map() and use the            // result as conditional.            Self::Template(_) => Err(self),            Self::Any(_) => Err(self),            Self::AnyList(_) => Err(self),        }    }     fn try_into_integer(self) -> Result<BoxedTemplateProperty<'a, i64>, Self> {        match self {            Self::Integer(property) => Ok(property),            Self::IntegerOpt(property) => Ok(property.try_unwrap("Integer").into_dyn()),            _ => Err(self),        }    }     fn try_into_timestamp(self) -> Result<BoxedTemplateProperty<'a, Timestamp>, Self> {        match self {            Self::Timestamp(property) => Ok(property),            _ => Err(self),        }    }     fn try_into_serialize(self) -> Option<BoxedSerializeProperty<'a>> {        match self {            Self::ByteString(property) => Some(property.into_serialize()),            Self::ByteStringList(property) => Some(property.into_serialize()),            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::ConfigValueOpt(property) => Some(                property                    .map(|opt| opt.map(config::to_serializable_value))                    .into_serialize(),            ),            Self::FsPath(property) => Some(property.into_serialize()),            Self::FsPathOpt(property) => Some(property.into_serialize()),            Self::Signature(property) => Some(property.into_serialize()),            Self::Email(property) => Some(property.into_serialize()),            Self::SizeHint(property) => Some(property.into_serialize()),            Self::RegexCaptures(_) => None,            Self::Timestamp(property) => Some(property.into_serialize()),            Self::TimestampRange(property) => Some(property.into_serialize()),            Self::Template(_) => None,            Self::Any(property) => property.try_into_serialize(),            Self::AnyList(property) => property.try_into_serialize(),        }    }     fn try_into_template(self) -> Option<Box<dyn Template + 'a>> {        match self {            Self::ByteString(property) => Some(property.into_template()),            Self::ByteStringList(property) => Some(property.into_template()),            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::ConfigValueOpt(property) => Some(property.into_template()),            Self::FsPath(property) => Some(property.into_template()),            Self::FsPathOpt(property) => Some(property.into_template()),            Self::Signature(property) => Some(property.into_template()),            Self::Email(property) => Some(property.into_template()),            Self::SizeHint(_) => None,            Self::RegexCaptures(_) => None,            Self::Timestamp(property) => Some(property.into_template()),            Self::TimestampRange(property) => Some(property.into_template()),            Self::Template(template) => Some(template),            Self::Any(property) => property.try_into_template(),            Self::AnyList(property) => property.try_into_template(),        }    }     fn try_into_eq(self, other: Self) -> Option<BoxedTemplateProperty<'a, bool>> {        match (self, other) {            (Self::ByteString(lhs), Self::ByteString(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l == r).into_dyn())            }            (Self::ByteString(lhs), Self::String(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l == r).into_dyn())            }            (Self::String(lhs), Self::ByteString(rhs)) => {                Some((lhs, rhs).map(|(l, r)| l == r).into_dyn())            }            (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::ByteString(_), _) => None,            (Self::ByteStringList(_), _) => None,            (Self::String(_), _) => None,            (Self::StringList(_), _) => None,            (Self::Boolean(_), _) => None,            (Self::Integer(_), _) => None,            (Self::IntegerOpt(_), _) => None,            (Self::ConfigValue(_), _) => None,            (Self::ConfigValueOpt(_), _) => None,            (Self::FsPath(_), _) => None,            (Self::FsPathOpt(_), _) => None,            (Self::Signature(_), _) => None,            (Self::Email(_), _) => None,            (Self::SizeHint(_), _) => None,            (Self::RegexCaptures(_), _) => None,            (Self::Timestamp(_), _) => None,            (Self::TimestampRange(_), _) => None,            (Self::Template(_), _) => None,            (Self::Any(_), _) => None,            (Self::AnyList(_), _) => None,        }    }     fn try_into_cmp(self, other: Self) -> Option<BoxedTemplateProperty<'a, Ordering>> {

Lines 1–500 of 5939.