3364 lines · 135.6 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. //! Template environment for `jj log`, `jj evolog` and similar. use std::any::Any;use std::cmp::Ordering;use std::cmp::max;use std::collections::HashMap;use std::fmt;use std::fmt::Display;use std::io;use std::path::Path;use std::path::PathBuf;use std::rc::Rc;use std::sync::Arc; use bstr::BString;use futures::StreamExt as _;use futures::TryStreamExt as _;use futures::stream::BoxStream;use itertools::Itertools as _;use jj_lib::backend::BackendResult;use jj_lib::backend::ChangeId;use jj_lib::backend::CommitId;use jj_lib::backend::MergedTreeValue;use jj_lib::backend::MergedTreeValueExt as _;use jj_lib::backend::Timestamp;use jj_lib::backend::TreeValue;use jj_lib::commit::Commit;use jj_lib::conflict_labels::ConflictLabels;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::evolution::CommitEvolutionEntry;use jj_lib::extensions_map::ExtensionsMap;use jj_lib::fileset;use jj_lib::fileset::FilesetDiagnostics;use jj_lib::fileset::FilesetExpression;use jj_lib::fileset::FilesetParseContext;use jj_lib::id_prefix::IdPrefixContext;use jj_lib::id_prefix::IdPrefixIndex;use jj_lib::index::IndexResult;use jj_lib::matchers::Matcher;use jj_lib::merge::Diff;use jj_lib::merged_tree::MergedTree;use jj_lib::object_id::ObjectId as _;use jj_lib::op_store::LocalRemoteRefTarget;use jj_lib::op_store::OperationId;use jj_lib::op_store::RefTarget;use jj_lib::op_store::RemoteRef;use jj_lib::ref_name::RefName;use jj_lib::ref_name::WorkspaceName;use jj_lib::ref_name::WorkspaceNameBuf;use jj_lib::repo::Repo;use jj_lib::repo::RepoLoader;use jj_lib::repo_path::RepoPathBuf;use jj_lib::revset;use jj_lib::revset::Revset;use jj_lib::revset::RevsetContainingFn;use jj_lib::revset::RevsetDiagnostics;use jj_lib::revset::RevsetParseContext;use jj_lib::revset::UserRevsetExpression;use jj_lib::rewrite::rebase_to_dest_parent;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 jj_lib::ui_path::RepoPathUiConverter;use jj_lib::workspace::DefaultWorkspaceLoaderFactory;use jj_lib::workspace::WorkspaceLoaderFactory as _;use jj_lib::workspace_store::SimpleWorkspaceStore;use jj_lib::workspace_store::WorkspaceStore as _;use once_cell::unsync::OnceCell;use pollster::FutureExt as _;use serde::Serialize as _; use crate::diff_util;use crate::diff_util::DiffStatEntry;use crate::diff_util::DiffStatOptions;use crate::diff_util::DiffStats;use crate::formatter::Formatter;use crate::operation_templater;use crate::operation_templater::OperationTemplateBuildFnTable;use crate::operation_templater::OperationTemplateEnvironment;use crate::operation_templater::OperationTemplatePropertyKind;use crate::operation_templater::OperationTemplatePropertyVar;use crate::revset_util;use crate::template_builder;use crate::template_builder::BuildContext;use crate::template_builder::CoreTemplateBuildFnTable;use crate::template_builder::CoreTemplatePropertyKind;use crate::template_builder::CoreTemplatePropertyVar;use crate::template_builder::TemplateBuildFunctionFnMap;use crate::template_builder::TemplateBuildMethodFnMap;use crate::template_builder::TemplateLanguage;use crate::template_builder::expect_stringify_expression;use crate::template_builder::merge_fn_map;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::BoxedAnyProperty;use crate::templater::BoxedSerializeProperty;use crate::templater::BoxedTemplateProperty;use crate::templater::Literal;use crate::templater::SizeHint;use crate::templater::Template;use crate::templater::TemplateFormatter;use crate::templater::TemplatePropertyError;use crate::templater::TemplatePropertyExt as _; pub trait CommitTemplateLanguageExtension {    fn build_fn_table<'repo>(&self) -> CommitTemplateBuildFnTable<'repo>;     fn build_cache_extensions(&self, extensions: &mut ExtensionsMap);} /// Template environment for `jj log` and `jj evolog`.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 in a way that    // Revset/FilesetParseContext are constructed from a common resource?    // RepoPathUiConverter and WorkspaceName are contained in RevsetParseContext    // for example.    revset_parse_context: RevsetParseContext<'repo>,    id_prefix_context: &'repo IdPrefixContext,    immutable_expression: Arc<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: Arc<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,        }    }     fn fileset_parse_context(&self) -> FilesetParseContext<'repo> {        FilesetParseContext {            aliases_map: self.revset_parse_context.fileset_aliases_map,            path_converter: self.path_converter,        }    }} impl<'repo> TemplateLanguage<'repo> for CommitTemplateLanguage<'repo> {    type Property = CommitTemplatePropertyKind<'repo>;     fn settings(&self) -> &UserSettings {        self.repo.base_repo().settings()    }     fn current_dir(&self) -> &Path {        let RepoPathUiConverter::Fs { cwd, base: _ } = self.path_converter;        cwd    }     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::Operation(property) => {                let table = &self.build_fn_table.operation;                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).into_dyn();                build(self, diagnostics, build_ctx, inner_property, function)            }            CommitTemplatePropertyKind::CommitList(property) => {                let table = &self.build_fn_table.commit_list_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                build(self, diagnostics, build_ctx, property, function)            }            CommitTemplatePropertyKind::CommitEvolutionEntry(property) => {                let table = &self.build_fn_table.commit_evolution_entry_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                build(self, diagnostics, build_ctx, property, function)            }            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).into_dyn();                build(self, diagnostics, build_ctx, inner_property, function)            }            CommitTemplatePropertyKind::CommitRefList(property) => {                let table = &self.build_fn_table.commit_ref_list_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                build(self, diagnostics, build_ctx, property, function)            }            CommitTemplatePropertyKind::WorkspaceRef(property) => {                let table = &self.build_fn_table.workspace_ref_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                build(self, diagnostics, build_ctx, property, function)            }            CommitTemplatePropertyKind::WorkspaceRefOpt(property) => {                let type_name = "WorkspaceRef";                let table = &self.build_fn_table.workspace_ref_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                let inner_property = property.try_unwrap(type_name).into_dyn();                build(self, diagnostics, build_ctx, inner_property, function)            }            CommitTemplatePropertyKind::WorkspaceRefList(property) => {                let table = &self.build_fn_table.workspace_ref_list_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                build(self, diagnostics, build_ctx, property, function)            }            CommitTemplatePropertyKind::RefSymbol(property) => {                let table = &self.build_fn_table.core.string_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                let inner_property = property.map(|RefSymbolBuf(s)| s).into_dyn();                build(self, diagnostics, build_ctx, inner_property, function)            }            CommitTemplatePropertyKind::RefSymbolOpt(property) => {                let type_name = "RefSymbol";                let table = &self.build_fn_table.core.string_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                let inner_property = property                    .try_unwrap(type_name)                    .map(|RefSymbolBuf(s)| s)                    .into_dyn();                build(self, diagnostics, build_ctx, inner_property, function)            }            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).into_dyn();                build(self, diagnostics, build_ctx, inner_property, function)            }            CommitTemplatePropertyKind::ChangeId(property) => {                let table = &self.build_fn_table.change_id_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                build(self, diagnostics, build_ctx, property, function)            }            CommitTemplatePropertyKind::CommitId(property) => {                let table = &self.build_fn_table.commit_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) => {                let table = &self.build_fn_table.tree_diff_entry_list_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                build(self, diagnostics, build_ctx, property, function)            }            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::TreeEntryList(property) => {                let table = &self.build_fn_table.tree_entry_list_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 = property.map(|formatted| formatted.stats).into_dyn();                build(self, diagnostics, build_ctx, property, function)            }            CommitTemplatePropertyKind::DiffStatEntry(property) => {                let table = &self.build_fn_table.diff_stat_entry_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                build(self, diagnostics, build_ctx, property, function)            }            CommitTemplatePropertyKind::DiffStatEntryList(property) => {                let table = &self.build_fn_table.diff_stat_entry_list_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                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).into_dyn();                build(self, diagnostics, build_ctx, 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) => {                let table = &self.build_fn_table.trailer_list_methods;                let build = template_parser::lookup_method(type_name, table, function)?;                build(self, diagnostics, build_ctx, property, function)            }        }    }} // 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>()    }} impl OperationTemplateEnvironment for CommitTemplateLanguage<'_> {    fn repo_loader(&self) -> &RepoLoader {        self.repo.base_repo().loader()    }     fn current_op_id(&self) -> Option<&OperationId> {        // TODO: Maybe return None if the repo is a MutableRepo?        Some(self.repo.base_repo().op_id())    }} pub enum CommitTemplatePropertyKind<'repo> {    Core(CoreTemplatePropertyKind<'repo>),    Operation(OperationTemplatePropertyKind<'repo>),    Commit(BoxedTemplateProperty<'repo, Commit>),    CommitOpt(BoxedTemplateProperty<'repo, Option<Commit>>),    CommitList(BoxedTemplateProperty<'repo, Vec<Commit>>),    CommitEvolutionEntry(BoxedTemplateProperty<'repo, CommitEvolutionEntry>),    CommitRef(BoxedTemplateProperty<'repo, Rc<CommitRef>>),    CommitRefOpt(BoxedTemplateProperty<'repo, Option<Rc<CommitRef>>>),    CommitRefList(BoxedTemplateProperty<'repo, Vec<Rc<CommitRef>>>),    WorkspaceRef(BoxedTemplateProperty<'repo, WorkspaceRef>),    WorkspaceRefOpt(BoxedTemplateProperty<'repo, Option<WorkspaceRef>>),    WorkspaceRefList(BoxedTemplateProperty<'repo, Vec<WorkspaceRef>>),    RefSymbol(BoxedTemplateProperty<'repo, RefSymbolBuf>),    RefSymbolOpt(BoxedTemplateProperty<'repo, Option<RefSymbolBuf>>),    RepoPath(BoxedTemplateProperty<'repo, RepoPathBuf>),    RepoPathOpt(BoxedTemplateProperty<'repo, Option<RepoPathBuf>>),    ChangeId(BoxedTemplateProperty<'repo, ChangeId>),    CommitId(BoxedTemplateProperty<'repo, CommitId>),    ShortestIdPrefix(BoxedTemplateProperty<'repo, ShortestIdPrefix>),    TreeDiff(BoxedTemplateProperty<'repo, TreeDiff>),    TreeDiffEntry(BoxedTemplateProperty<'repo, TreeDiffEntry>),    TreeDiffEntryList(BoxedTemplateProperty<'repo, Vec<TreeDiffEntry>>),    TreeEntry(BoxedTemplateProperty<'repo, TreeEntry>),    TreeEntryList(BoxedTemplateProperty<'repo, Vec<TreeEntry>>),    DiffStats(BoxedTemplateProperty<'repo, DiffStatsFormatted<'repo>>),    DiffStatEntry(BoxedTemplateProperty<'repo, DiffStatEntry>),    DiffStatEntryList(BoxedTemplateProperty<'repo, Vec<DiffStatEntry>>),    CryptographicSignatureOpt(BoxedTemplateProperty<'repo, Option<CryptographicSignature>>),    AnnotationLine(BoxedTemplateProperty<'repo, AnnotationLine>),    Trailer(BoxedTemplateProperty<'repo, Trailer>),    TrailerList(BoxedTemplateProperty<'repo, Vec<Trailer>>),} template_builder::impl_core_property_wrappers!(<'repo> CommitTemplatePropertyKind<'repo> => Core);operation_templater::impl_operation_property_wrappers!(<'repo> CommitTemplatePropertyKind<'repo> => Operation);template_builder::impl_property_wrappers!(<'repo> CommitTemplatePropertyKind<'repo> {    Commit(Commit),    CommitOpt(Option<Commit>),    CommitList(Vec<Commit>),    CommitEvolutionEntry(CommitEvolutionEntry),    CommitRef(Rc<CommitRef>),    CommitRefOpt(Option<Rc<CommitRef>>),    CommitRefList(Vec<Rc<CommitRef>>),    WorkspaceRef(WorkspaceRef),    WorkspaceRefOpt(Option<WorkspaceRef>),    WorkspaceRefList(Vec<WorkspaceRef>),    RefSymbol(RefSymbolBuf),    RefSymbolOpt(Option<RefSymbolBuf>),    RepoPath(RepoPathBuf),    RepoPathOpt(Option<RepoPathBuf>),    ChangeId(ChangeId),    CommitId(CommitId),    ShortestIdPrefix(ShortestIdPrefix),    TreeDiff(TreeDiff),    TreeDiffEntry(TreeDiffEntry),

Lines 1–500 of 3364.