ok/jj
1
0
Fork 0
forked from mirrors/jj

templater: translate keywords to "self" methods by core template engine

This eliminates the separate keywords table. All keywords are resolved through
the pseudo "self" property. Maybe we'll add "self" keyword/variable later.
This commit is contained in:
Yuya Nishihara 2024-02-22 17:44:37 +09:00
parent 6e5eff5423
commit e80b906188
3 changed files with 37 additions and 45 deletions

View file

@ -54,8 +54,9 @@ impl<'repo> TemplateLanguage<'repo> for CommitTemplateLanguage<'repo, '_> {
template_builder::impl_core_wrap_property_fns!('repo, CommitTemplatePropertyKind::Core);
fn build_keyword(&self, name: &str, span: pest::Span) -> TemplateParseResult<Self::Property> {
build_commit_keyword(self, name, span)
fn build_self(&self) -> Self::Property {
// Commit object is lightweight (a few Arc + CommitId)
self.wrap_commit(TemplatePropertyFn(|commit: &Commit| commit.clone()))
}
fn build_method(
@ -236,21 +237,6 @@ impl CommitKeywordCache {
}
}
fn build_commit_keyword<'repo>(
language: &CommitTemplateLanguage<'repo, '_>,
name: &str,
span: pest::Span,
) -> TemplateParseResult<CommitTemplatePropertyKind<'repo>> {
// Commit object is lightweight (a few Arc + CommitId), so just clone it
// to turn into a property type. Abstraction over "for<'a> (&'a T) -> &'a T"
// and "(&T) -> T" wouldn't be simple. If we want to remove Clone/Rc/Arc,
// maybe we can add an abstraction that takes "Fn(&Commit) -> O" and returns
// "TemplateProperty<Commit, Output = O>".
let property = TemplatePropertyFn(|commit: &Commit| commit.clone());
build_commit_keyword_opt(language, property, name)
.ok_or_else(|| TemplateParseError::no_such_keyword(name, span))
}
fn build_commit_method<'repo>(
language: &CommitTemplateLanguage<'repo, '_>,
_build_ctx: &BuildContext<CommitTemplatePropertyKind<'repo>>,
@ -265,6 +251,7 @@ fn build_commit_method<'repo>(
}
}
// TODO: merge into build_commit_method()
fn build_commit_keyword_opt<'repo>(
language: &CommitTemplateLanguage<'repo, '_>,
property: impl TemplateProperty<Commit, Output = Commit> + 'repo,

View file

@ -42,8 +42,9 @@ impl TemplateLanguage<'static> for OperationTemplateLanguage<'_> {
template_builder::impl_core_wrap_property_fns!('static, OperationTemplatePropertyKind::Core);
fn build_keyword(&self, name: &str, span: pest::Span) -> TemplateParseResult<Self::Property> {
build_operation_keyword(self, name, span)
fn build_self(&self) -> Self::Property {
// Operation object is lightweight (a few Arc + OperationId)
self.wrap_operation(TemplatePropertyFn(|op: &Operation| op.clone()))
}
fn build_method(
@ -67,7 +68,6 @@ impl TemplateLanguage<'static> for OperationTemplateLanguage<'_> {
}
impl OperationTemplateLanguage<'_> {
#[allow(unused)] // TODO
fn wrap_operation(
&self,
property: impl TemplateProperty<Operation, Output = Operation> + 'static,
@ -124,18 +124,6 @@ impl IntoTemplateProperty<'static, Operation> for OperationTemplatePropertyKind
}
}
fn build_operation_keyword(
language: &OperationTemplateLanguage,
name: &str,
span: pest::Span,
) -> TemplateParseResult<OperationTemplatePropertyKind> {
// Operation object is lightweight (a few Arc + OperationId), so just clone
// it to turn into a property type.
let property = TemplatePropertyFn(|op: &Operation| op.clone());
build_operation_keyword_opt(language, property, name)
.ok_or_else(|| TemplateParseError::no_such_keyword(name, span))
}
fn build_operation_method(
language: &OperationTemplateLanguage,
_build_ctx: &BuildContext<OperationTemplatePropertyKind>,
@ -150,6 +138,7 @@ fn build_operation_method(
}
}
// TODO: merge into build_operation_method()
fn build_operation_keyword_opt(
language: &OperationTemplateLanguage,
property: impl TemplateProperty<Operation, Output = Operation> + 'static,

View file

@ -68,7 +68,10 @@ pub trait TemplateLanguage<'a> {
template: Box<dyn ListTemplate<Self::Context> + 'a>,
) -> Self::Property;
fn build_keyword(&self, name: &str, span: pest::Span) -> TemplateParseResult<Self::Property>;
/// Creates the `self` template property, which is usually a function that
/// clones the `Context` object.
fn build_self(&self) -> Self::Property;
fn build_method(
&self,
build_ctx: &BuildContext<Self::Property>,
@ -272,6 +275,28 @@ pub struct BuildContext<'i, P> {
local_variables: HashMap<&'i str, &'i (dyn Fn() -> P)>,
}
fn build_keyword<'a, L: TemplateLanguage<'a>>(
language: &L,
build_ctx: &BuildContext<L::Property>,
name: &str,
name_span: pest::Span<'_>,
) -> TemplateParseResult<Expression<L::Property>> {
// Keyword is a 0-ary method on the "self" property
let self_property = language.build_self();
let function = FunctionCallNode {
name,
name_span,
args: vec![],
args_span: name_span.end_pos().span(&name_span.end_pos()),
};
let property = language
.build_method(build_ctx, self_property, &function)
// Since keyword is a 0-ary method, any argument-related errors mean
// there's no such keyword.
.map_err(|_| TemplateParseError::no_such_keyword(name, name_span))?;
Ok(Expression::with_label(property, name))
}
fn build_unary_operation<'a, L: TemplateLanguage<'a>>(
language: &L,
build_ctx: &BuildContext<L::Property>,
@ -842,8 +867,7 @@ pub fn build_expression<'a, L: TemplateLanguage<'a>>(
// Don't label a local variable with its name
Ok(Expression::unlabeled(make()))
} else {
let property = language.build_keyword(name, node.span)?;
Ok(Expression::with_label(property, *name))
build_keyword(language, build_ctx, name, node.span)
}
}
ExpressionKind::Boolean(value) => {
@ -960,15 +984,8 @@ mod tests {
impl_core_wrap_property_fns!('static, TestTemplatePropertyKind::Core);
fn build_keyword(
&self,
name: &str,
span: pest::Span,
) -> TemplateParseResult<Self::Property> {
self.keywords
.get(name)
.map(|f| f(self))
.ok_or_else(|| TemplateParseError::no_such_keyword(name, span))
fn build_self(&self) -> Self::Property {
TestTemplatePropertyKind::Unit
}
fn build_method(
@ -995,7 +1012,6 @@ mod tests {
enum TestTemplatePropertyKind {
Core(CoreTemplatePropertyKind<'static, ()>),
#[allow(unused)] // TODO
Unit,
}