// 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::any::Any;use std::cmp::max;use std::cmp::Ordering;use std::collections::HashMap;use std::io;use std::rc::Rc; use bstr::BString;use futures::stream::BoxStream;use futures::StreamExt as _;use futures::TryStreamExt as _;use itertools::Itertools as _;use jj_lib::backend::BackendResult;use jj_lib::backend::ChangeId;use jj_lib::backend::CommitId;use jj_lib::backend::TreeValue;use jj_lib::commit::Commit;use jj_lib::conflicts;use jj_lib::conflicts::ConflictMarkerStyle;use jj_lib::copies::CopiesTreeDiffEntry;use jj_lib::copies::CopiesTreeDiffEntryPath;use jj_lib::copies::CopyRecords;use jj_lib::extensions_map::ExtensionsMap;use jj_lib::fileset;use jj_lib::fileset::FilesetDiagnostics;use jj_lib::fileset::FilesetExpression;use jj_lib::id_prefix::IdPrefixContext;use jj_lib::id_prefix::IdPrefixIndex;use jj_lib::matchers::Matcher;use jj_lib::merge::MergedTreeValue;use jj_lib::merged_tree::MergedTree;use jj_lib::object_id::ObjectId as _;use jj_lib::op_store::RefTarget;use jj_lib::op_store::RemoteRef;use jj_lib::ref_name::WorkspaceName;use jj_lib::ref_name::WorkspaceNameBuf;use jj_lib::repo::Repo;use jj_lib::repo_path::RepoPathBuf;use jj_lib::repo_path::RepoPathUiConverter;use jj_lib::revset;use jj_lib::revset::Revset;use jj_lib::revset::RevsetContainingFn;use jj_lib::revset::RevsetDiagnostics;use jj_lib::revset::RevsetModifier;use jj_lib::revset::RevsetParseContext;use jj_lib::revset::UserRevsetExpression;use jj_lib::settings::UserSettings;use jj_lib::signing::SigStatus;use jj_lib::signing::SignError;use jj_lib::signing::SignResult;use jj_lib::signing::Verification;use jj_lib::store::Store;use jj_lib::trailer;use jj_lib::trailer::Trailer;use once_cell::unsync::OnceCell;use pollster::FutureExt as _; use crate::diff_util;use crate::diff_util::DiffStats;use crate::formatter::Formatter;use crate::revset_util;use crate::template_builder;use crate::template_builder::expect_plain_text_expression;use crate::template_builder::merge_fn_map;use crate::template_builder::BuildContext;use crate::template_builder::CoreTemplateBuildFnTable;use crate::template_builder::CoreTemplatePropertyKind;use crate::template_builder::IntoTemplateProperty;use crate::template_builder::TemplateBuildMethodFnMap;use crate::template_builder::TemplateLanguage;use crate::template_parser;use crate::template_parser::ExpressionNode;use crate::template_parser::FunctionCallNode;use crate::template_parser::TemplateDiagnostics;use crate::template_parser::TemplateParseError;use crate::template_parser::TemplateParseResult;use crate::templater;use crate::templater::PlainTextFormattedProperty;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::text_util; pub trait CommitTemplateLanguageExtension { fn build_fn_table<'repo>(&self) -> CommitTemplateBuildFnTable<'repo>; fn build_cache_extensions(&self, extensions: &mut ExtensionsMap);} pub struct CommitTemplateLanguage<'repo> { repo: &'repo dyn Repo, path_converter: &'repo RepoPathUiConverter, workspace_name: WorkspaceNameBuf, // RevsetParseContext doesn't borrow a repo, but we'll need 'repo lifetime // anyway to capture it to evaluate dynamically-constructed user expression // such as `revset("ancestors(" ++ commit_id ++ ")")`. // TODO: Maybe refactor context structs? RepoPathUiConverter and // WorkspaceName are contained in RevsetParseContext for example. revset_parse_context: RevsetParseContext<'repo>, id_prefix_context: &'repo IdPrefixContext, immutable_expression: Rc<UserRevsetExpression>, conflict_marker_style: ConflictMarkerStyle, build_fn_table: CommitTemplateBuildFnTable<'repo>, keyword_cache: CommitKeywordCache<'repo>, cache_extensions: ExtensionsMap,} impl<'repo> CommitTemplateLanguage<'repo> { /// Sets up environment where commit template will be transformed to /// evaluation tree. #[expect(clippy::too_many_arguments)] pub fn new( repo: &'repo dyn Repo, path_converter: &'repo RepoPathUiConverter, workspace_name: &WorkspaceName, revset_parse_context: RevsetParseContext<'repo>, id_prefix_context: &'repo IdPrefixContext, immutable_expression: Rc<UserRevsetExpression>, conflict_marker_style: ConflictMarkerStyle, extensions: &[impl AsRef<dyn CommitTemplateLanguageExtension>], ) -> Self { let mut build_fn_table = CommitTemplateBuildFnTable::builtin(); let mut cache_extensions = ExtensionsMap::empty(); for extension in extensions { build_fn_table.merge(extension.as_ref().build_fn_table()); extension .as_ref() .build_cache_extensions(&mut cache_extensions); } CommitTemplateLanguage { repo, path_converter, workspace_name: workspace_name.to_owned(), revset_parse_context, id_prefix_context, immutable_expression, conflict_marker_style, build_fn_table, keyword_cache: CommitKeywordCache::default(), cache_extensions, } }} impl<'repo> TemplateLanguage<'repo> for CommitTemplateLanguage<'repo> { type Property = CommitTemplatePropertyKind<'repo>; template_builder::impl_core_wrap_property_fns!('repo, CommitTemplatePropertyKind::Core); fn settings(&self) -> &UserSettings { self.repo.base_repo().settings() } fn build_function( &self, diagnostics: &mut TemplateDiagnostics, build_ctx: &BuildContext<Self::Property>, function: &FunctionCallNode, ) -> TemplateParseResult<Self::Property> { let table = &self.build_fn_table.core; table.build_function(self, diagnostics, build_ctx, function) } fn build_method( &self, diagnostics: &mut TemplateDiagnostics, build_ctx: &BuildContext<Self::Property>, property: Self::Property, function: &FunctionCallNode, ) -> TemplateParseResult<Self::Property> { let type_name = property.type_name(); match property { CommitTemplatePropertyKind::Core(property) => { let table = &self.build_fn_table.core; table.build_method(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::Commit(property) => { let table = &self.build_fn_table.commit_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::CommitOpt(property) => { let type_name = "Commit"; let table = &self.build_fn_table.commit_methods; let build = template_parser::lookup_method(type_name, table, function)?; let inner_property = property.try_unwrap(type_name); build( self, diagnostics, build_ctx, Box::new(inner_property), function, ) } CommitTemplatePropertyKind::CommitList(property) => { // TODO: migrate to table? template_builder::build_unformattable_list_method( self, diagnostics, build_ctx, property, function, Self::wrap_commit, Self::wrap_commit_list, ) } CommitTemplatePropertyKind::CommitRef(property) => { let table = &self.build_fn_table.commit_ref_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::CommitRefOpt(property) => { let type_name = "CommitRef"; let table = &self.build_fn_table.commit_ref_methods; let build = template_parser::lookup_method(type_name, table, function)?; let inner_property = property.try_unwrap(type_name); build( self, diagnostics, build_ctx, Box::new(inner_property), function, ) } CommitTemplatePropertyKind::CommitRefList(property) => { // TODO: migrate to table? template_builder::build_formattable_list_method( self, diagnostics, build_ctx, property, function, Self::wrap_commit_ref, Self::wrap_commit_ref_list, ) } CommitTemplatePropertyKind::RepoPath(property) => { let table = &self.build_fn_table.repo_path_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::RepoPathOpt(property) => { let type_name = "RepoPath"; let table = &self.build_fn_table.repo_path_methods; let build = template_parser::lookup_method(type_name, table, function)?; let inner_property = property.try_unwrap(type_name); build( self, diagnostics, build_ctx, Box::new(inner_property), function, ) } CommitTemplatePropertyKind::CommitOrChangeId(property) => { let table = &self.build_fn_table.commit_or_change_id_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::ShortestIdPrefix(property) => { let table = &self.build_fn_table.shortest_id_prefix_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::TreeDiff(property) => { let table = &self.build_fn_table.tree_diff_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::TreeDiffEntry(property) => { let table = &self.build_fn_table.tree_diff_entry_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::TreeDiffEntryList(property) => { // TODO: migrate to table? template_builder::build_unformattable_list_method( self, diagnostics, build_ctx, property, function, Self::wrap_tree_diff_entry, Self::wrap_tree_diff_entry_list, ) } CommitTemplatePropertyKind::TreeEntry(property) => { let table = &self.build_fn_table.tree_entry_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::DiffStats(property) => { let table = &self.build_fn_table.diff_stats_methods; let build = template_parser::lookup_method(type_name, table, function)?; // Strip off formatting parameters which are needed only for the // default template output. let property = Box::new(property.map(|formatted| formatted.stats)); build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::CryptographicSignatureOpt(property) => { let type_name = "CryptographicSignature"; let table = &self.build_fn_table.cryptographic_signature_methods; let build = template_parser::lookup_method(type_name, table, function)?; let inner_property = property.try_unwrap(type_name); build( self, diagnostics, build_ctx, Box::new(inner_property), function, ) } CommitTemplatePropertyKind::AnnotationLine(property) => { let type_name = "AnnotationLine"; let table = &self.build_fn_table.annotation_line_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::Trailer(property) => { let table = &self.build_fn_table.trailer_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::TrailerList(property) => { // TODO: migrate to table? if function.name == "contains_key" { let [key_node] = function.expect_exact_arguments()?; let key_property = expect_plain_text_expression(self, diagnostics, build_ctx, key_node)?; let out_property = (property, key_property) .map(|(trailers, key)| trailers.iter().any(|t| t.key == key)); Ok(Self::wrap_boolean(out_property)) } else { template_builder::build_formattable_list_method( self, diagnostics, build_ctx, property, function, Self::wrap_trailer, Self::wrap_trailer_list, ) } } } }} // If we need to add multiple languages that support Commit types, this can be// turned into a trait which extends TemplateLanguage.impl<'repo> CommitTemplateLanguage<'repo> { pub fn repo(&self) -> &'repo dyn Repo { self.repo } pub fn workspace_name(&self) -> &WorkspaceName { &self.workspace_name } pub fn keyword_cache(&self) -> &CommitKeywordCache<'repo> { &self.keyword_cache } pub fn cache_extension<T: Any>(&self) -> Option<&T> { self.cache_extensions.get::<T>() } pub fn wrap_commit( property: impl TemplateProperty<Output = Commit> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::Commit(Box::new(property)) } pub fn wrap_commit_opt( property: impl TemplateProperty<Output = Option<Commit>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CommitOpt(Box::new(property)) } pub fn wrap_commit_list( property: impl TemplateProperty<Output = Vec<Commit>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CommitList(Box::new(property)) } pub fn wrap_commit_ref( property: impl TemplateProperty<Output = Rc<CommitRef>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CommitRef(Box::new(property)) } pub fn wrap_commit_ref_opt( property: impl TemplateProperty<Output = Option<Rc<CommitRef>>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CommitRefOpt(Box::new(property)) } pub fn wrap_commit_ref_list( property: impl TemplateProperty<Output = Vec<Rc<CommitRef>>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CommitRefList(Box::new(property)) } pub fn wrap_repo_path( property: impl TemplateProperty<Output = RepoPathBuf> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::RepoPath(Box::new(property)) } pub fn wrap_repo_path_opt( property: impl TemplateProperty<Output = Option<RepoPathBuf>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::RepoPathOpt(Box::new(property)) } pub fn wrap_commit_or_change_id( property: impl TemplateProperty<Output = CommitOrChangeId> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CommitOrChangeId(Box::new(property)) } pub fn wrap_shortest_id_prefix( property: impl TemplateProperty<Output = ShortestIdPrefix> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::ShortestIdPrefix(Box::new(property)) } pub fn wrap_tree_diff( property: impl TemplateProperty<Output = TreeDiff> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::TreeDiff(Box::new(property)) } pub fn wrap_tree_diff_entry( property: impl TemplateProperty<Output = TreeDiffEntry> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::TreeDiffEntry(Box::new(property)) } pub fn wrap_tree_diff_entry_list( property: impl TemplateProperty<Output = Vec<TreeDiffEntry>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::TreeDiffEntryList(Box::new(property)) } pub fn wrap_tree_entry( property: impl TemplateProperty<Output = TreeEntry> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::TreeEntry(Box::new(property)) } pub fn wrap_diff_stats( property: impl TemplateProperty<Output = DiffStatsFormatted<'repo>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::DiffStats(Box::new(property)) } fn wrap_cryptographic_signature_opt( property: impl TemplateProperty<Output = Option<CryptographicSignature>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CryptographicSignatureOpt(Box::new(property)) } pub fn wrap_annotation_line( property: impl TemplateProperty<Output = AnnotationLine> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::AnnotationLine(Box::new(property)) } pub fn wrap_trailer( property: impl TemplateProperty<Output = Trailer> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::Trailer(Box::new(property)) } pub fn wrap_trailer_list( property: impl TemplateProperty<Output = Vec<Trailer>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::TrailerList(Box::new(property)) }}Lines 1–500 of 2369. Load more
// 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::any::Any; use std::cmp::max; use std::cmp::Ordering; use std::collections::HashMap; use std::io; use std::rc::Rc; use bstr::BString; use futures::stream::BoxStream; use futures::StreamExt as _; use futures::TryStreamExt as _; use itertools::Itertools as _; use jj_lib::backend::BackendResult; use jj_lib::backend::ChangeId; use jj_lib::backend::CommitId; use jj_lib::backend::TreeValue; use jj_lib::commit::Commit; use jj_lib::conflicts; use jj_lib::conflicts::ConflictMarkerStyle; use jj_lib::copies::CopiesTreeDiffEntry; use jj_lib::copies::CopiesTreeDiffEntryPath; use jj_lib::copies::CopyRecords; use jj_lib::extensions_map::ExtensionsMap; use jj_lib::fileset; use jj_lib::fileset::FilesetDiagnostics; use jj_lib::fileset::FilesetExpression; use jj_lib::id_prefix::IdPrefixContext; use jj_lib::id_prefix::IdPrefixIndex; use jj_lib::matchers::Matcher; use jj_lib::merge::MergedTreeValue; use jj_lib::merged_tree::MergedTree; use jj_lib::object_id::ObjectId as _; use jj_lib::op_store::RefTarget; use jj_lib::op_store::RemoteRef; use jj_lib::ref_name::WorkspaceName; use jj_lib::ref_name::WorkspaceNameBuf; use jj_lib::repo::Repo; use jj_lib::repo_path::RepoPathBuf; use jj_lib::repo_path::RepoPathUiConverter; use jj_lib::revset; use jj_lib::revset::Revset; use jj_lib::revset::RevsetContainingFn; use jj_lib::revset::RevsetDiagnostics; use jj_lib::revset::RevsetModifier; use jj_lib::revset::RevsetParseContext; use jj_lib::revset::UserRevsetExpression; use jj_lib::settings::UserSettings; use jj_lib::signing::SigStatus; use jj_lib::signing::SignError; use jj_lib::signing::SignResult; use jj_lib::signing::Verification; use jj_lib::store::Store; use jj_lib::trailer; use jj_lib::trailer::Trailer; use once_cell::unsync::OnceCell; use pollster::FutureExt as _; use crate::diff_util; use crate::diff_util::DiffStats; use crate::formatter::Formatter; use crate::revset_util; use crate::template_builder; use crate::template_builder::expect_plain_text_expression; use crate::template_builder::merge_fn_map; use crate::template_builder::BuildContext; use crate::template_builder::CoreTemplateBuildFnTable; use crate::template_builder::CoreTemplatePropertyKind; use crate::template_builder::IntoTemplateProperty; use crate::template_builder::TemplateBuildMethodFnMap; use crate::template_builder::TemplateLanguage; use crate::template_parser; use crate::template_parser::ExpressionNode; use crate::template_parser::FunctionCallNode; use crate::template_parser::TemplateDiagnostics; use crate::template_parser::TemplateParseError; use crate::template_parser::TemplateParseResult; use crate::templater; use crate::templater::PlainTextFormattedProperty; 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::text_util; pub trait CommitTemplateLanguageExtension { fn build_fn_table<'repo>(&self) -> CommitTemplateBuildFnTable<'repo>; fn build_cache_extensions(&self, extensions: &mut ExtensionsMap); } pub struct CommitTemplateLanguage<'repo> { repo: &'repo dyn Repo, path_converter: &'repo RepoPathUiConverter, workspace_name: WorkspaceNameBuf, // RevsetParseContext doesn't borrow a repo, but we'll need 'repo lifetime // anyway to capture it to evaluate dynamically-constructed user expression // such as `revset("ancestors(" ++ commit_id ++ ")")`. // TODO: Maybe refactor context structs? RepoPathUiConverter and // WorkspaceName are contained in RevsetParseContext for example. revset_parse_context: RevsetParseContext<'repo>, id_prefix_context: &'repo IdPrefixContext, immutable_expression: Rc<UserRevsetExpression>, conflict_marker_style: ConflictMarkerStyle, build_fn_table: CommitTemplateBuildFnTable<'repo>, keyword_cache: CommitKeywordCache<'repo>, cache_extensions: ExtensionsMap, } impl<'repo> CommitTemplateLanguage<'repo> { /// Sets up environment where commit template will be transformed to /// evaluation tree. #[expect(clippy::too_many_arguments)] pub fn new( repo: &'repo dyn Repo, path_converter: &'repo RepoPathUiConverter, workspace_name: &WorkspaceName, revset_parse_context: RevsetParseContext<'repo>, id_prefix_context: &'repo IdPrefixContext, immutable_expression: Rc<UserRevsetExpression>, conflict_marker_style: ConflictMarkerStyle, extensions: &[impl AsRef<dyn CommitTemplateLanguageExtension>], ) -> Self { let mut build_fn_table = CommitTemplateBuildFnTable::builtin(); let mut cache_extensions = ExtensionsMap::empty(); for extension in extensions { build_fn_table.merge(extension.as_ref().build_fn_table()); extension .as_ref() .build_cache_extensions(&mut cache_extensions); } CommitTemplateLanguage { repo, path_converter, workspace_name: workspace_name.to_owned(), revset_parse_context, id_prefix_context, immutable_expression, conflict_marker_style, build_fn_table, keyword_cache: CommitKeywordCache::default(), cache_extensions, } } } impl<'repo> TemplateLanguage<'repo> for CommitTemplateLanguage<'repo> { type Property = CommitTemplatePropertyKind<'repo>; template_builder::impl_core_wrap_property_fns!('repo, CommitTemplatePropertyKind::Core); fn settings(&self) -> &UserSettings { self.repo.base_repo().settings() } fn build_function( &self, diagnostics: &mut TemplateDiagnostics, build_ctx: &BuildContext<Self::Property>, function: &FunctionCallNode, ) -> TemplateParseResult<Self::Property> { let table = &self.build_fn_table.core; table.build_function(self, diagnostics, build_ctx, function) } fn build_method( &self, diagnostics: &mut TemplateDiagnostics, build_ctx: &BuildContext<Self::Property>, property: Self::Property, function: &FunctionCallNode, ) -> TemplateParseResult<Self::Property> { let type_name = property.type_name(); match property { CommitTemplatePropertyKind::Core(property) => { let table = &self.build_fn_table.core; table.build_method(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::Commit(property) => { let table = &self.build_fn_table.commit_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::CommitOpt(property) => { let type_name = "Commit"; let table = &self.build_fn_table.commit_methods; let build = template_parser::lookup_method(type_name, table, function)?; let inner_property = property.try_unwrap(type_name); build( self, diagnostics, build_ctx, Box::new(inner_property), function, ) } CommitTemplatePropertyKind::CommitList(property) => { // TODO: migrate to table? template_builder::build_unformattable_list_method( self, diagnostics, build_ctx, property, function, Self::wrap_commit, Self::wrap_commit_list, ) } CommitTemplatePropertyKind::CommitRef(property) => { let table = &self.build_fn_table.commit_ref_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::CommitRefOpt(property) => { let type_name = "CommitRef"; let table = &self.build_fn_table.commit_ref_methods; let build = template_parser::lookup_method(type_name, table, function)?; let inner_property = property.try_unwrap(type_name); build( self, diagnostics, build_ctx, Box::new(inner_property), function, ) } CommitTemplatePropertyKind::CommitRefList(property) => { // TODO: migrate to table? template_builder::build_formattable_list_method( self, diagnostics, build_ctx, property, function, Self::wrap_commit_ref, Self::wrap_commit_ref_list, ) } CommitTemplatePropertyKind::RepoPath(property) => { let table = &self.build_fn_table.repo_path_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::RepoPathOpt(property) => { let type_name = "RepoPath"; let table = &self.build_fn_table.repo_path_methods; let build = template_parser::lookup_method(type_name, table, function)?; let inner_property = property.try_unwrap(type_name); build( self, diagnostics, build_ctx, Box::new(inner_property), function, ) } CommitTemplatePropertyKind::CommitOrChangeId(property) => { let table = &self.build_fn_table.commit_or_change_id_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::ShortestIdPrefix(property) => { let table = &self.build_fn_table.shortest_id_prefix_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::TreeDiff(property) => { let table = &self.build_fn_table.tree_diff_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::TreeDiffEntry(property) => { let table = &self.build_fn_table.tree_diff_entry_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::TreeDiffEntryList(property) => { // TODO: migrate to table? template_builder::build_unformattable_list_method( self, diagnostics, build_ctx, property, function, Self::wrap_tree_diff_entry, Self::wrap_tree_diff_entry_list, ) } CommitTemplatePropertyKind::TreeEntry(property) => { let table = &self.build_fn_table.tree_entry_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::DiffStats(property) => { let table = &self.build_fn_table.diff_stats_methods; let build = template_parser::lookup_method(type_name, table, function)?; // Strip off formatting parameters which are needed only for the // default template output. let property = Box::new(property.map(|formatted| formatted.stats)); build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::CryptographicSignatureOpt(property) => { let type_name = "CryptographicSignature"; let table = &self.build_fn_table.cryptographic_signature_methods; let build = template_parser::lookup_method(type_name, table, function)?; let inner_property = property.try_unwrap(type_name); build( self, diagnostics, build_ctx, Box::new(inner_property), function, ) } CommitTemplatePropertyKind::AnnotationLine(property) => { let type_name = "AnnotationLine"; let table = &self.build_fn_table.annotation_line_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::Trailer(property) => { let table = &self.build_fn_table.trailer_methods; let build = template_parser::lookup_method(type_name, table, function)?; build(self, diagnostics, build_ctx, property, function) } CommitTemplatePropertyKind::TrailerList(property) => { // TODO: migrate to table? if function.name == "contains_key" { let [key_node] = function.expect_exact_arguments()?; let key_property = expect_plain_text_expression(self, diagnostics, build_ctx, key_node)?; let out_property = (property, key_property) .map(|(trailers, key)| trailers.iter().any(|t| t.key == key)); Ok(Self::wrap_boolean(out_property)) } else { template_builder::build_formattable_list_method( self, diagnostics, build_ctx, property, function, Self::wrap_trailer, Self::wrap_trailer_list, ) } } } } } // If we need to add multiple languages that support Commit types, this can be // turned into a trait which extends TemplateLanguage. impl<'repo> CommitTemplateLanguage<'repo> { pub fn repo(&self) -> &'repo dyn Repo { self.repo } pub fn workspace_name(&self) -> &WorkspaceName { &self.workspace_name } pub fn keyword_cache(&self) -> &CommitKeywordCache<'repo> { &self.keyword_cache } pub fn cache_extension<T: Any>(&self) -> Option<&T> { self.cache_extensions.get::<T>() } pub fn wrap_commit( property: impl TemplateProperty<Output = Commit> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::Commit(Box::new(property)) } pub fn wrap_commit_opt( property: impl TemplateProperty<Output = Option<Commit>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CommitOpt(Box::new(property)) } pub fn wrap_commit_list( property: impl TemplateProperty<Output = Vec<Commit>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CommitList(Box::new(property)) } pub fn wrap_commit_ref( property: impl TemplateProperty<Output = Rc<CommitRef>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CommitRef(Box::new(property)) } pub fn wrap_commit_ref_opt( property: impl TemplateProperty<Output = Option<Rc<CommitRef>>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CommitRefOpt(Box::new(property)) } pub fn wrap_commit_ref_list( property: impl TemplateProperty<Output = Vec<Rc<CommitRef>>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CommitRefList(Box::new(property)) } pub fn wrap_repo_path( property: impl TemplateProperty<Output = RepoPathBuf> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::RepoPath(Box::new(property)) } pub fn wrap_repo_path_opt( property: impl TemplateProperty<Output = Option<RepoPathBuf>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::RepoPathOpt(Box::new(property)) } pub fn wrap_commit_or_change_id( property: impl TemplateProperty<Output = CommitOrChangeId> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CommitOrChangeId(Box::new(property)) } pub fn wrap_shortest_id_prefix( property: impl TemplateProperty<Output = ShortestIdPrefix> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::ShortestIdPrefix(Box::new(property)) } pub fn wrap_tree_diff( property: impl TemplateProperty<Output = TreeDiff> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::TreeDiff(Box::new(property)) } pub fn wrap_tree_diff_entry( property: impl TemplateProperty<Output = TreeDiffEntry> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::TreeDiffEntry(Box::new(property)) } pub fn wrap_tree_diff_entry_list( property: impl TemplateProperty<Output = Vec<TreeDiffEntry>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::TreeDiffEntryList(Box::new(property)) } pub fn wrap_tree_entry( property: impl TemplateProperty<Output = TreeEntry> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::TreeEntry(Box::new(property)) } pub fn wrap_diff_stats( property: impl TemplateProperty<Output = DiffStatsFormatted<'repo>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::DiffStats(Box::new(property)) } fn wrap_cryptographic_signature_opt( property: impl TemplateProperty<Output = Option<CryptographicSignature>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::CryptographicSignatureOpt(Box::new(property)) } pub fn wrap_annotation_line( property: impl TemplateProperty<Output = AnnotationLine> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::AnnotationLine(Box::new(property)) } pub fn wrap_trailer( property: impl TemplateProperty<Output = Trailer> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::Trailer(Box::new(property)) } pub fn wrap_trailer_list( property: impl TemplateProperty<Output = Vec<Trailer>> + 'repo, ) -> CommitTemplatePropertyKind<'repo> { CommitTemplatePropertyKind::TrailerList(Box::new(property)) } }