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

templater: migrate operation template methods to symbol table

This commit is contained in:
Yuya Nishihara 2024-02-25 14:54:32 +09:00
parent 10acfdcb86
commit 9ad2fee72b

View file

@ -21,11 +21,10 @@ use jj_lib::operation::Operation;
use crate::formatter::Formatter; use crate::formatter::Formatter;
use crate::template_builder::{ use crate::template_builder::{
self, BuildContext, CoreTemplatePropertyKind, IntoTemplateProperty, TemplateLanguage, self, BuildContext, CoreTemplatePropertyKind, IntoTemplateProperty, TemplateBuildMethodFnMap,
}; TemplateLanguage,
use crate::template_parser::{
self, FunctionCallNode, TemplateAliasesMap, TemplateParseError, TemplateParseResult,
}; };
use crate::template_parser::{self, FunctionCallNode, TemplateAliasesMap, TemplateParseResult};
use crate::templater::{ use crate::templater::{
IntoTemplate, PlainTextFormattedProperty, Template, TemplateFunction, TemplateProperty, IntoTemplate, PlainTextFormattedProperty, Template, TemplateFunction, TemplateProperty,
TemplatePropertyFn, TimestampRange, TemplatePropertyFn, TimestampRange,
@ -34,6 +33,7 @@ use crate::templater::{
struct OperationTemplateLanguage { struct OperationTemplateLanguage {
root_op_id: OperationId, root_op_id: OperationId,
current_op_id: Option<OperationId>, current_op_id: Option<OperationId>,
build_fn_table: OperationTemplateBuildFnTable,
} }
impl TemplateLanguage<'static> for OperationTemplateLanguage { impl TemplateLanguage<'static> for OperationTemplateLanguage {
@ -58,10 +58,14 @@ impl TemplateLanguage<'static> for OperationTemplateLanguage {
template_builder::build_core_method(self, build_ctx, property, function) template_builder::build_core_method(self, build_ctx, property, function)
} }
OperationTemplatePropertyKind::Operation(property) => { OperationTemplatePropertyKind::Operation(property) => {
build_operation_method(self, build_ctx, property, function) let table = &self.build_fn_table.operation_methods;
let build = template_parser::lookup_method("Operation", table, function)?;
build(self, build_ctx, property, function)
} }
OperationTemplatePropertyKind::OperationId(property) => { OperationTemplatePropertyKind::OperationId(property) => {
build_operation_id_method(self, build_ctx, property, function) let table = &self.build_fn_table.operation_id_methods;
let build = template_parser::lookup_method("OperationId", table, function)?;
build(self, build_ctx, property, function)
} }
} }
} }
@ -124,67 +128,91 @@ impl IntoTemplateProperty<'static, Operation> for OperationTemplatePropertyKind
} }
} }
fn build_operation_method( /// Table of functions that translate method call node of self type `T`.
language: &OperationTemplateLanguage, type OperationTemplateBuildMethodFnMap<T> =
_build_ctx: &BuildContext<OperationTemplatePropertyKind>, TemplateBuildMethodFnMap<'static, OperationTemplateLanguage, T>;
self_property: impl TemplateProperty<Operation, Output = Operation> + 'static,
function: &FunctionCallNode, /// Symbol table of methods available in the operation template.
) -> TemplateParseResult<OperationTemplatePropertyKind> { struct OperationTemplateBuildFnTable {
let property = match function.name { // TODO: add core methods/functions table
"current_operation" => { operation_methods: OperationTemplateBuildMethodFnMap<Operation>,
operation_id_methods: OperationTemplateBuildMethodFnMap<OperationId>,
}
impl OperationTemplateBuildFnTable {
/// Creates new symbol table containing the builtin methods.
fn builtin() -> Self {
OperationTemplateBuildFnTable {
operation_methods: builtin_operation_methods(),
operation_id_methods: builtin_operation_id_methods(),
}
}
}
fn builtin_operation_methods() -> OperationTemplateBuildMethodFnMap<Operation> {
// Not using maplit::hashmap!{} or custom declarative macro here because
// code completion inside macro is quite restricted.
let mut map = OperationTemplateBuildMethodFnMap::<Operation>::new();
map.insert(
"current_operation",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?; template_parser::expect_no_arguments(function)?;
let current_op_id = language.current_op_id.clone(); let current_op_id = language.current_op_id.clone();
language.wrap_boolean(TemplateFunction::new(self_property, move |op| { let out_property = TemplateFunction::new(self_property, move |op| {
Some(op.id()) == current_op_id.as_ref() Some(op.id()) == current_op_id.as_ref()
})) });
} Ok(language.wrap_boolean(out_property))
"description" => { },
);
map.insert(
"description",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?; template_parser::expect_no_arguments(function)?;
language.wrap_string(TemplateFunction::new(self_property, |op| { let out_property =
op.metadata().description.clone() TemplateFunction::new(self_property, |op| op.metadata().description.clone());
})) Ok(language.wrap_string(out_property))
} },
"id" => { );
map.insert("id", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?; template_parser::expect_no_arguments(function)?;
language.wrap_operation_id(TemplateFunction::new(self_property, |op| op.id().clone())) let out_property = TemplateFunction::new(self_property, |op| op.id().clone());
} Ok(language.wrap_operation_id(out_property))
"tags" => { });
map.insert("tags", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?; template_parser::expect_no_arguments(function)?;
language.wrap_string(TemplateFunction::new(self_property, |op| { let out_property = TemplateFunction::new(self_property, |op| {
// TODO: introduce map type // TODO: introduce map type
op.metadata() op.metadata()
.tags .tags
.iter() .iter()
.map(|(key, value)| format!("{key}: {value}")) .map(|(key, value)| format!("{key}: {value}"))
.join("\n") .join("\n")
})) });
} Ok(language.wrap_string(out_property))
"time" => { });
map.insert("time", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?; template_parser::expect_no_arguments(function)?;
language.wrap_timestamp_range(TemplateFunction::new(self_property, |op| { let out_property = TemplateFunction::new(self_property, |op| TimestampRange {
TimestampRange {
start: op.metadata().start_time.clone(), start: op.metadata().start_time.clone(),
end: op.metadata().end_time.clone(), end: op.metadata().end_time.clone(),
} });
})) Ok(language.wrap_timestamp_range(out_property))
} });
"user" => { map.insert("user", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?; template_parser::expect_no_arguments(function)?;
language.wrap_string(TemplateFunction::new(self_property, |op| { let out_property = TemplateFunction::new(self_property, |op| {
// TODO: introduce dedicated type and provide accessors? // TODO: introduce dedicated type and provide accessors?
format!("{}@{}", op.metadata().username, op.metadata().hostname) format!("{}@{}", op.metadata().username, op.metadata().hostname)
})) });
} Ok(language.wrap_string(out_property))
"root" => { });
map.insert("root", |language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?; template_parser::expect_no_arguments(function)?;
let root_op_id = language.root_op_id.clone(); let root_op_id = language.root_op_id.clone();
language.wrap_boolean(TemplateFunction::new(self_property, move |op| { let out_property = TemplateFunction::new(self_property, move |op| op.id() == &root_op_id);
op.id() == &root_op_id Ok(language.wrap_boolean(out_property))
})) });
} map
_ => return Err(TemplateParseError::no_such_method("Operation", function)),
};
Ok(property)
} }
impl Template<()> for OperationId { impl Template<()> for OperationId {
@ -193,30 +221,23 @@ impl Template<()> for OperationId {
} }
} }
fn build_operation_id_method( fn builtin_operation_id_methods() -> OperationTemplateBuildMethodFnMap<OperationId> {
language: &OperationTemplateLanguage, // Not using maplit::hashmap!{} or custom declarative macro here because
build_ctx: &BuildContext<OperationTemplatePropertyKind>, // code completion inside macro is quite restricted.
self_property: impl TemplateProperty<Operation, Output = OperationId> + 'static, let mut map = OperationTemplateBuildMethodFnMap::<OperationId>::new();
function: &FunctionCallNode, map.insert("short", |language, build_ctx, self_property, function| {
) -> TemplateParseResult<OperationTemplatePropertyKind> {
let property = match function.name {
"short" => {
let ([], [len_node]) = template_parser::expect_arguments(function)?; let ([], [len_node]) = template_parser::expect_arguments(function)?;
let len_property = len_node let len_property = len_node
.map(|node| template_builder::expect_integer_expression(language, build_ctx, node)) .map(|node| template_builder::expect_integer_expression(language, build_ctx, node))
.transpose()?; .transpose()?;
language.wrap_string(TemplateFunction::new( let out_property = TemplateFunction::new((self_property, len_property), |(id, len)| {
(self_property, len_property),
|(id, len)| {
let mut hex = id.hex(); let mut hex = id.hex();
hex.truncate(len.map_or(12, |l| l.try_into().unwrap_or(0))); hex.truncate(len.map_or(12, |l| l.try_into().unwrap_or(0)));
hex hex
}, });
)) Ok(language.wrap_string(out_property))
} });
_ => return Err(TemplateParseError::no_such_method("OperationId", function)), map
};
Ok(property)
} }
pub fn parse( pub fn parse(
@ -228,6 +249,7 @@ pub fn parse(
let language = OperationTemplateLanguage { let language = OperationTemplateLanguage {
root_op_id: root_op_id.clone(), root_op_id: root_op_id.clone(),
current_op_id: current_op_id.cloned(), current_op_id: current_op_id.cloned(),
build_fn_table: OperationTemplateBuildFnTable::builtin(),
}; };
let node = template_parser::parse(template_text, aliases_map)?; let node = template_parser::parse(template_text, aliases_map)?;
template_builder::build(&language, &node) template_builder::build(&language, &node)