templater: wrap RefName with Rc to copy OnceCell around

Because template is declarative language, and is evaluated as a tree, there
will be multiple copies of the same RefName object. This patch allows us to
cache ahead/behind counts which will be lazily calculated.
This commit is contained in:
Yuya Nishihara 2024-04-30 17:56:02 +09:00
parent cb0964b1d0
commit ccabb11890

View File

@ -217,19 +217,19 @@ impl<'repo> CommitTemplateLanguage<'repo> {
} }
pub fn wrap_ref_name( pub fn wrap_ref_name(
property: impl TemplateProperty<Output = RefName> + 'repo, property: impl TemplateProperty<Output = Rc<RefName>> + 'repo,
) -> CommitTemplatePropertyKind<'repo> { ) -> CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::RefName(Box::new(property)) CommitTemplatePropertyKind::RefName(Box::new(property))
} }
pub fn wrap_ref_name_opt( pub fn wrap_ref_name_opt(
property: impl TemplateProperty<Output = Option<RefName>> + 'repo, property: impl TemplateProperty<Output = Option<Rc<RefName>>> + 'repo,
) -> CommitTemplatePropertyKind<'repo> { ) -> CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::RefNameOpt(Box::new(property)) CommitTemplatePropertyKind::RefNameOpt(Box::new(property))
} }
pub fn wrap_ref_name_list( pub fn wrap_ref_name_list(
property: impl TemplateProperty<Output = Vec<RefName>> + 'repo, property: impl TemplateProperty<Output = Vec<Rc<RefName>>> + 'repo,
) -> CommitTemplatePropertyKind<'repo> { ) -> CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::RefNameList(Box::new(property)) CommitTemplatePropertyKind::RefNameList(Box::new(property))
} }
@ -252,9 +252,9 @@ pub enum CommitTemplatePropertyKind<'repo> {
Commit(Box<dyn TemplateProperty<Output = Commit> + 'repo>), Commit(Box<dyn TemplateProperty<Output = Commit> + 'repo>),
CommitOpt(Box<dyn TemplateProperty<Output = Option<Commit>> + 'repo>), CommitOpt(Box<dyn TemplateProperty<Output = Option<Commit>> + 'repo>),
CommitList(Box<dyn TemplateProperty<Output = Vec<Commit>> + 'repo>), CommitList(Box<dyn TemplateProperty<Output = Vec<Commit>> + 'repo>),
RefName(Box<dyn TemplateProperty<Output = RefName> + 'repo>), RefName(Box<dyn TemplateProperty<Output = Rc<RefName>> + 'repo>),
RefNameOpt(Box<dyn TemplateProperty<Output = Option<RefName>> + 'repo>), RefNameOpt(Box<dyn TemplateProperty<Output = Option<Rc<RefName>>> + 'repo>),
RefNameList(Box<dyn TemplateProperty<Output = Vec<RefName>> + 'repo>), RefNameList(Box<dyn TemplateProperty<Output = Vec<Rc<RefName>>> + 'repo>),
CommitOrChangeId(Box<dyn TemplateProperty<Output = CommitOrChangeId> + 'repo>), CommitOrChangeId(Box<dyn TemplateProperty<Output = CommitOrChangeId> + 'repo>),
ShortestIdPrefix(Box<dyn TemplateProperty<Output = ShortestIdPrefix> + 'repo>), ShortestIdPrefix(Box<dyn TemplateProperty<Output = ShortestIdPrefix> + 'repo>),
} }
@ -340,7 +340,7 @@ pub type CommitTemplateBuildMethodFnMap<'repo, T> =
pub struct CommitTemplateBuildFnTable<'repo> { pub struct CommitTemplateBuildFnTable<'repo> {
pub core: CoreTemplateBuildFnTable<'repo, CommitTemplateLanguage<'repo>>, pub core: CoreTemplateBuildFnTable<'repo, CommitTemplateLanguage<'repo>>,
pub commit_methods: CommitTemplateBuildMethodFnMap<'repo, Commit>, pub commit_methods: CommitTemplateBuildMethodFnMap<'repo, Commit>,
pub ref_name_methods: CommitTemplateBuildMethodFnMap<'repo, RefName>, pub ref_name_methods: CommitTemplateBuildMethodFnMap<'repo, Rc<RefName>>,
pub commit_or_change_id_methods: CommitTemplateBuildMethodFnMap<'repo, CommitOrChangeId>, pub commit_or_change_id_methods: CommitTemplateBuildMethodFnMap<'repo, CommitOrChangeId>,
pub shortest_id_prefix_methods: CommitTemplateBuildMethodFnMap<'repo, ShortestIdPrefix>, pub shortest_id_prefix_methods: CommitTemplateBuildMethodFnMap<'repo, ShortestIdPrefix>,
} }
@ -721,7 +721,7 @@ fn evaluate_user_revset<'repo>(
} }
/// Branch or tag name with metadata. /// Branch or tag name with metadata.
#[derive(Clone, Debug)] #[derive(Debug)]
pub struct RefName { pub struct RefName {
/// Local name. /// Local name.
name: String, name: String,
@ -735,26 +735,29 @@ pub struct RefName {
} }
impl RefName { impl RefName {
// RefName is wrapped by Rc<T> to make it cheaply cloned and share
// lazy-evaluation results across clones.
/// Creates local ref representation which might track some of the /// Creates local ref representation which might track some of the
/// `remote_refs`. /// `remote_refs`.
pub fn local<'a>( pub fn local<'a>(
name: impl Into<String>, name: impl Into<String>,
target: RefTarget, target: RefTarget,
remote_refs: impl IntoIterator<Item = &'a RemoteRef>, remote_refs: impl IntoIterator<Item = &'a RemoteRef>,
) -> Self { ) -> Rc<Self> {
let synced = remote_refs let synced = remote_refs
.into_iter() .into_iter()
.all(|remote_ref| !remote_ref.is_tracking() || remote_ref.target == target); .all(|remote_ref| !remote_ref.is_tracking() || remote_ref.target == target);
RefName { Rc::new(RefName {
name: name.into(), name: name.into(),
remote: None, remote: None,
target, target,
synced, synced,
} })
} }
/// Creates local ref representation which doesn't track any remote refs. /// Creates local ref representation which doesn't track any remote refs.
pub fn local_only(name: impl Into<String>, target: RefTarget) -> Self { pub fn local_only(name: impl Into<String>, target: RefTarget) -> Rc<Self> {
Self::local(name, target, []) Self::local(name, target, [])
} }
@ -765,14 +768,14 @@ impl RefName {
remote_name: impl Into<String>, remote_name: impl Into<String>,
remote_ref: RemoteRef, remote_ref: RemoteRef,
local_target: &RefTarget, local_target: &RefTarget,
) -> Self { ) -> Rc<Self> {
let synced = remote_ref.is_tracking() && remote_ref.target == *local_target; let synced = remote_ref.is_tracking() && remote_ref.target == *local_target;
RefName { Rc::new(RefName {
name: name.into(), name: name.into(),
remote: Some(remote_name.into()), remote: Some(remote_name.into()),
target: remote_ref.target, target: remote_ref.target,
synced, synced,
} })
} }
/// Creates remote ref representation which isn't tracked by a local ref. /// Creates remote ref representation which isn't tracked by a local ref.
@ -780,13 +783,13 @@ impl RefName {
name: impl Into<String>, name: impl Into<String>,
remote_name: impl Into<String>, remote_name: impl Into<String>,
target: RefTarget, target: RefTarget,
) -> Self { ) -> Rc<Self> {
RefName { Rc::new(RefName {
name: name.into(), name: name.into(),
remote: Some(remote_name.into()), remote: Some(remote_name.into()),
target, target,
synced: false, // has no local counterpart synced: false, // has no local counterpart
} })
} }
fn is_local(&self) -> bool { fn is_local(&self) -> bool {
@ -807,7 +810,8 @@ impl RefName {
} }
} }
impl Template for RefName { // If wrapping with Rc<T> becomes common, add generic impl for Rc<T>.
impl Template for Rc<RefName> {
fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
write!(formatter.labeled("name"), "{}", self.name)?; write!(formatter.labeled("name"), "{}", self.name)?;
if let Some(remote) = &self.remote { if let Some(remote) = &self.remote {
@ -825,27 +829,28 @@ impl Template for RefName {
} }
} }
impl Template for Vec<RefName> { impl Template for Vec<Rc<RefName>> {
fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> { fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
templater::format_joined(formatter, self, " ") templater::format_joined(formatter, self, " ")
} }
} }
fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, RefName> { fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Rc<RefName>> {
type L<'repo> = CommitTemplateLanguage<'repo>; type L<'repo> = CommitTemplateLanguage<'repo>;
// Not using maplit::hashmap!{} or custom declarative macro here because // Not using maplit::hashmap!{} or custom declarative macro here because
// code completion inside macro is quite restricted. // code completion inside macro is quite restricted.
let mut map = CommitTemplateBuildMethodFnMap::<RefName>::new(); let mut map = CommitTemplateBuildMethodFnMap::<Rc<RefName>>::new();
map.insert("name", |_language, _build_ctx, self_property, function| { map.insert("name", |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?; template_parser::expect_no_arguments(function)?;
let out_property = self_property.map(|ref_name| ref_name.name); let out_property = self_property.map(|ref_name| ref_name.name.clone());
Ok(L::wrap_string(out_property)) Ok(L::wrap_string(out_property))
}); });
map.insert( map.insert(
"remote", "remote",
|_language, _build_ctx, self_property, function| { |_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?; template_parser::expect_no_arguments(function)?;
let out_property = self_property.map(|ref_name| ref_name.remote.unwrap_or_default()); let out_property =
self_property.map(|ref_name| ref_name.remote.clone().unwrap_or_default());
Ok(L::wrap_string(out_property)) Ok(L::wrap_string(out_property))
}, },
); );
@ -907,11 +912,11 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Re
/// Cache for reverse lookup refs. /// Cache for reverse lookup refs.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct RefNamesIndex { pub struct RefNamesIndex {
index: HashMap<CommitId, Vec<RefName>>, index: HashMap<CommitId, Vec<Rc<RefName>>>,
} }
impl RefNamesIndex { impl RefNamesIndex {
fn insert<'a>(&mut self, ids: impl IntoIterator<Item = &'a CommitId>, name: RefName) { fn insert<'a>(&mut self, ids: impl IntoIterator<Item = &'a CommitId>, name: Rc<RefName>) {
for id in ids { for id in ids {
let ref_names = self.index.entry(id.clone()).or_default(); let ref_names = self.index.entry(id.clone()).or_default();
ref_names.push(name.clone()); ref_names.push(name.clone());
@ -920,7 +925,7 @@ impl RefNamesIndex {
#[allow(unknown_lints)] // XXX FIXME (aseipp): nightly bogons; re-test this occasionally #[allow(unknown_lints)] // XXX FIXME (aseipp): nightly bogons; re-test this occasionally
#[allow(clippy::manual_unwrap_or_default)] #[allow(clippy::manual_unwrap_or_default)]
pub fn get(&self, id: &CommitId) -> &[RefName] { pub fn get(&self, id: &CommitId) -> &[Rc<RefName>] {
if let Some(names) = self.index.get(id) { if let Some(names) = self.index.get(id) {
names names
} else { } else {
@ -962,7 +967,7 @@ fn build_ref_names_index<'a>(
index index
} }
fn extract_git_head(repo: &dyn Repo, commit: &Commit) -> Option<RefName> { fn extract_git_head(repo: &dyn Repo, commit: &Commit) -> Option<Rc<RefName>> {
let target = repo.view().git_head(); let target = repo.view().git_head();
target target
.added_ids() .added_ids()