forked from mirrors/jj
templater: migrate operation template methods to symbol table
This commit is contained in:
parent
10acfdcb86
commit
9ad2fee72b
1 changed files with 109 additions and 87 deletions
|
@ -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" => {
|
);
|
||||||
template_parser::expect_no_arguments(function)?;
|
map.insert("id", |language, _build_ctx, self_property, function| {
|
||||||
language.wrap_operation_id(TemplateFunction::new(self_property, |op| op.id().clone()))
|
template_parser::expect_no_arguments(function)?;
|
||||||
}
|
let out_property = TemplateFunction::new(self_property, |op| op.id().clone());
|
||||||
"tags" => {
|
Ok(language.wrap_operation_id(out_property))
|
||||||
template_parser::expect_no_arguments(function)?;
|
});
|
||||||
language.wrap_string(TemplateFunction::new(self_property, |op| {
|
map.insert("tags", |language, _build_ctx, self_property, function| {
|
||||||
// TODO: introduce map type
|
template_parser::expect_no_arguments(function)?;
|
||||||
op.metadata()
|
let out_property = TemplateFunction::new(self_property, |op| {
|
||||||
.tags
|
// TODO: introduce map type
|
||||||
.iter()
|
op.metadata()
|
||||||
.map(|(key, value)| format!("{key}: {value}"))
|
.tags
|
||||||
.join("\n")
|
.iter()
|
||||||
}))
|
.map(|(key, value)| format!("{key}: {value}"))
|
||||||
}
|
.join("\n")
|
||||||
"time" => {
|
});
|
||||||
template_parser::expect_no_arguments(function)?;
|
Ok(language.wrap_string(out_property))
|
||||||
language.wrap_timestamp_range(TemplateFunction::new(self_property, |op| {
|
});
|
||||||
TimestampRange {
|
map.insert("time", |language, _build_ctx, self_property, function| {
|
||||||
start: op.metadata().start_time.clone(),
|
template_parser::expect_no_arguments(function)?;
|
||||||
end: op.metadata().end_time.clone(),
|
let out_property = TemplateFunction::new(self_property, |op| TimestampRange {
|
||||||
}
|
start: op.metadata().start_time.clone(),
|
||||||
}))
|
end: op.metadata().end_time.clone(),
|
||||||
}
|
});
|
||||||
"user" => {
|
Ok(language.wrap_timestamp_range(out_property))
|
||||||
template_parser::expect_no_arguments(function)?;
|
});
|
||||||
language.wrap_string(TemplateFunction::new(self_property, |op| {
|
map.insert("user", |language, _build_ctx, self_property, function| {
|
||||||
// TODO: introduce dedicated type and provide accessors?
|
template_parser::expect_no_arguments(function)?;
|
||||||
format!("{}@{}", op.metadata().username, op.metadata().hostname)
|
let out_property = TemplateFunction::new(self_property, |op| {
|
||||||
}))
|
// TODO: introduce dedicated type and provide accessors?
|
||||||
}
|
format!("{}@{}", op.metadata().username, op.metadata().hostname)
|
||||||
"root" => {
|
});
|
||||||
template_parser::expect_no_arguments(function)?;
|
Ok(language.wrap_string(out_property))
|
||||||
let root_op_id = language.root_op_id.clone();
|
});
|
||||||
language.wrap_boolean(TemplateFunction::new(self_property, move |op| {
|
map.insert("root", |language, _build_ctx, self_property, function| {
|
||||||
op.id() == &root_op_id
|
template_parser::expect_no_arguments(function)?;
|
||||||
}))
|
let root_op_id = language.root_op_id.clone();
|
||||||
}
|
let out_property = TemplateFunction::new(self_property, move |op| op.id() == &root_op_id);
|
||||||
_ => return Err(TemplateParseError::no_such_method("Operation", function)),
|
Ok(language.wrap_boolean(out_property))
|
||||||
};
|
});
|
||||||
Ok(property)
|
map
|
||||||
}
|
}
|
||||||
|
|
||||||
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 ([], [len_node]) = template_parser::expect_arguments(function)?;
|
||||||
let property = match function.name {
|
let len_property = len_node
|
||||||
"short" => {
|
.map(|node| template_builder::expect_integer_expression(language, build_ctx, node))
|
||||||
let ([], [len_node]) = template_parser::expect_arguments(function)?;
|
.transpose()?;
|
||||||
let len_property = len_node
|
let out_property = TemplateFunction::new((self_property, len_property), |(id, len)| {
|
||||||
.map(|node| template_builder::expect_integer_expression(language, build_ctx, node))
|
let mut hex = id.hex();
|
||||||
.transpose()?;
|
hex.truncate(len.map_or(12, |l| l.try_into().unwrap_or(0)));
|
||||||
language.wrap_string(TemplateFunction::new(
|
hex
|
||||||
(self_property, len_property),
|
});
|
||||||
|(id, len)| {
|
Ok(language.wrap_string(out_property))
|
||||||
let mut hex = id.hex();
|
});
|
||||||
hex.truncate(len.map_or(12, |l| l.try_into().unwrap_or(0)));
|
map
|
||||||
hex
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => return Err(TemplateParseError::no_such_method("OperationId", function)),
|
|
||||||
};
|
|
||||||
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)
|
||||||
|
|
Loading…
Reference in a new issue