mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-19 19:08:08 +00:00
commit_templater: support extensions of the template language
This commit is contained in:
parent
16755546bb
commit
570fd29ba3
3 changed files with 146 additions and 4 deletions
87
cli/examples/custom-commit-templater/main.rs
Normal file
87
cli/examples/custom-commit-templater/main.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2024 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.
|
||||
|
||||
use jj_cli::cli_util::CliRunner;
|
||||
use jj_cli::commit_templater::{CommitTemplateBuildFnTable, CommitTemplateLanguageExtension};
|
||||
use jj_cli::template_builder::TemplateLanguage;
|
||||
use jj_cli::template_parser::{self, TemplateParseError};
|
||||
use jj_cli::templater::{TemplateFunction, TemplatePropertyError};
|
||||
use jj_lib::commit::Commit;
|
||||
use jj_lib::object_id::ObjectId;
|
||||
|
||||
struct HexCounter;
|
||||
|
||||
fn num_digits_in_id(commit: Commit) -> Result<i64, TemplatePropertyError> {
|
||||
let mut count = 0;
|
||||
for ch in commit.id().hex().chars() {
|
||||
if ch.is_ascii_digit() {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
fn num_char_in_id(commit: Commit, ch_match: char) -> Result<i64, TemplatePropertyError> {
|
||||
let mut count = 0;
|
||||
for ch in commit.id().hex().chars() {
|
||||
if ch == ch_match {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
impl CommitTemplateLanguageExtension for HexCounter {
|
||||
fn build_fn_table<'repo>(&self) -> CommitTemplateBuildFnTable<'repo> {
|
||||
let mut table = CommitTemplateBuildFnTable::empty();
|
||||
table.commit_methods.insert(
|
||||
"num_digits_in_id",
|
||||
|language, _build_context, property, call| {
|
||||
template_parser::expect_no_arguments(call)?;
|
||||
Ok(language.wrap_integer(TemplateFunction::new(property, num_digits_in_id)))
|
||||
},
|
||||
);
|
||||
table.commit_methods.insert(
|
||||
"num_char_in_id",
|
||||
|language, _build_context, property, call| {
|
||||
let [string_arg] = template_parser::expect_exact_arguments(call)?;
|
||||
let char_arg =
|
||||
template_parser::expect_string_literal_with(string_arg, |string, span| {
|
||||
let chars: Vec<_> = string.chars().collect();
|
||||
match chars[..] {
|
||||
[ch] => Ok(ch),
|
||||
_ => Err(TemplateParseError::unexpected_expression(
|
||||
"Expected singular character argument",
|
||||
span,
|
||||
)),
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(
|
||||
language.wrap_integer(TemplateFunction::new(property, move |commit| {
|
||||
num_char_in_id(commit, char_arg)
|
||||
})),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
table
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> std::process::ExitCode {
|
||||
CliRunner::init()
|
||||
.set_commit_template_extension(Box::new(HexCounter))
|
||||
.run()
|
||||
}
|
|
@ -78,6 +78,7 @@ use tracing::instrument;
|
|||
use tracing_chrome::ChromeLayerBuilder;
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
use crate::commit_templater::CommitTemplateLanguageExtension;
|
||||
use crate::config::{
|
||||
new_config_path, AnnotatedValue, CommandNameAndArgs, ConfigSource, LayeredConfigs,
|
||||
};
|
||||
|
@ -590,6 +591,7 @@ pub struct CommandHelper {
|
|||
global_args: GlobalArgs,
|
||||
settings: UserSettings,
|
||||
layered_configs: LayeredConfigs,
|
||||
commit_template_extension: Option<Arc<dyn CommitTemplateLanguageExtension>>,
|
||||
maybe_workspace_loader: Result<WorkspaceLoader, CommandError>,
|
||||
store_factories: StoreFactories,
|
||||
working_copy_factories: HashMap<String, Box<dyn WorkingCopyFactory>>,
|
||||
|
@ -605,6 +607,7 @@ impl CommandHelper {
|
|||
global_args: GlobalArgs,
|
||||
settings: UserSettings,
|
||||
layered_configs: LayeredConfigs,
|
||||
commit_template_extension: Option<Arc<dyn CommitTemplateLanguageExtension>>,
|
||||
maybe_workspace_loader: Result<WorkspaceLoader, CommandError>,
|
||||
store_factories: StoreFactories,
|
||||
working_copy_factories: HashMap<String, Box<dyn WorkingCopyFactory>>,
|
||||
|
@ -621,6 +624,7 @@ impl CommandHelper {
|
|||
global_args,
|
||||
settings,
|
||||
layered_configs,
|
||||
commit_template_extension,
|
||||
maybe_workspace_loader,
|
||||
store_factories,
|
||||
working_copy_factories,
|
||||
|
@ -799,6 +803,7 @@ pub struct WorkspaceCommandHelper {
|
|||
settings: UserSettings,
|
||||
workspace: Workspace,
|
||||
user_repo: ReadonlyUserRepo,
|
||||
commit_template_extension: Option<Arc<dyn CommitTemplateLanguageExtension>>,
|
||||
revset_aliases_map: RevsetAliasesMap,
|
||||
template_aliases_map: TemplateAliasesMap,
|
||||
may_update_working_copy: bool,
|
||||
|
@ -823,6 +828,7 @@ impl WorkspaceCommandHelper {
|
|||
repo.as_ref(),
|
||||
workspace.workspace_id(),
|
||||
&id_prefix_context,
|
||||
command.commit_template_extension.as_deref(),
|
||||
&template_aliases_map,
|
||||
&command.settings,
|
||||
)?;
|
||||
|
@ -836,6 +842,7 @@ impl WorkspaceCommandHelper {
|
|||
settings: command.settings.clone(),
|
||||
workspace,
|
||||
user_repo: ReadonlyUserRepo::new(repo),
|
||||
commit_template_extension: command.commit_template_extension.clone(),
|
||||
revset_aliases_map,
|
||||
template_aliases_map,
|
||||
may_update_working_copy,
|
||||
|
@ -1263,6 +1270,7 @@ Set which revision the branch points to with `jj branch set {branch_name} -r <RE
|
|||
self.repo().as_ref(),
|
||||
self.workspace_id(),
|
||||
id_prefix_context,
|
||||
self.commit_template_extension.as_deref(),
|
||||
template_text,
|
||||
&self.template_aliases_map,
|
||||
)?;
|
||||
|
@ -1291,6 +1299,7 @@ Set which revision the branch points to with `jj branch set {branch_name} -r <RE
|
|||
self.repo().as_ref(),
|
||||
self.workspace_id(),
|
||||
id_prefix_context,
|
||||
self.commit_template_extension.as_deref(),
|
||||
&self.template_aliases_map,
|
||||
&self.settings,
|
||||
)
|
||||
|
@ -1792,6 +1801,7 @@ impl WorkspaceCommandTransaction<'_> {
|
|||
self.tx.repo(),
|
||||
self.helper.workspace_id(),
|
||||
&id_prefix_context,
|
||||
self.helper.commit_template_extension.as_deref(),
|
||||
&self.helper.template_aliases_map,
|
||||
&self.helper.settings,
|
||||
)
|
||||
|
@ -2152,6 +2162,7 @@ fn parse_commit_summary_template<'a>(
|
|||
repo: &'a dyn Repo,
|
||||
workspace_id: &WorkspaceId,
|
||||
id_prefix_context: &'a IdPrefixContext,
|
||||
extension: Option<&dyn CommitTemplateLanguageExtension>,
|
||||
aliases_map: &TemplateAliasesMap,
|
||||
settings: &UserSettings,
|
||||
) -> Result<Box<dyn Template<Commit> + 'a>, CommandError> {
|
||||
|
@ -2160,6 +2171,7 @@ fn parse_commit_summary_template<'a>(
|
|||
repo,
|
||||
workspace_id,
|
||||
id_prefix_context,
|
||||
extension,
|
||||
&template_text,
|
||||
aliases_map,
|
||||
)?)
|
||||
|
@ -2841,6 +2853,7 @@ pub struct CliRunner {
|
|||
extra_configs: Option<config::Config>,
|
||||
store_factories: Option<StoreFactories>,
|
||||
working_copy_factories: Option<HashMap<String, Box<dyn WorkingCopyFactory>>>,
|
||||
commit_template_extension: Option<Arc<dyn CommitTemplateLanguageExtension>>,
|
||||
dispatch_fn: CliDispatchFn,
|
||||
start_hook_fns: Vec<CliDispatchFn>,
|
||||
process_global_args_fns: Vec<ProcessGlobalArgsFn>,
|
||||
|
@ -2862,6 +2875,7 @@ impl CliRunner {
|
|||
extra_configs: None,
|
||||
store_factories: None,
|
||||
working_copy_factories: None,
|
||||
commit_template_extension: None,
|
||||
dispatch_fn: Box::new(crate::commands::run_command),
|
||||
start_hook_fns: vec![],
|
||||
process_global_args_fns: vec![],
|
||||
|
@ -2895,6 +2909,14 @@ impl CliRunner {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn set_commit_template_extension(
|
||||
mut self,
|
||||
commit_template_extension: Box<dyn CommitTemplateLanguageExtension>,
|
||||
) -> Self {
|
||||
self.commit_template_extension = Some(commit_template_extension.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_start_hook(mut self, start_hook_fn: CliDispatchFn) -> Self {
|
||||
self.start_hook_fns.push(start_hook_fn);
|
||||
self
|
||||
|
@ -3009,6 +3031,7 @@ impl CliRunner {
|
|||
args.global_args,
|
||||
settings,
|
||||
layered_configs,
|
||||
self.commit_template_extension,
|
||||
maybe_workspace_loader,
|
||||
self.store_factories.unwrap_or_default(),
|
||||
working_copy_factories,
|
||||
|
|
|
@ -30,8 +30,8 @@ use once_cell::unsync::OnceCell;
|
|||
|
||||
use crate::formatter::Formatter;
|
||||
use crate::template_builder::{
|
||||
self, BuildContext, CoreTemplateBuildFnTable, CoreTemplatePropertyKind, IntoTemplateProperty,
|
||||
TemplateBuildMethodFnMap, TemplateLanguage,
|
||||
self, merge_fn_map, BuildContext, CoreTemplateBuildFnTable, CoreTemplatePropertyKind,
|
||||
IntoTemplateProperty, TemplateBuildMethodFnMap, TemplateLanguage,
|
||||
};
|
||||
use crate::template_parser::{self, FunctionCallNode, TemplateAliasesMap, TemplateParseResult};
|
||||
use crate::templater::{
|
||||
|
@ -48,6 +48,10 @@ pub struct CommitTemplateLanguage<'repo> {
|
|||
keyword_cache: CommitKeywordCache,
|
||||
}
|
||||
|
||||
pub trait CommitTemplateLanguageExtension {
|
||||
fn build_fn_table<'repo>(&self) -> CommitTemplateBuildFnTable<'repo>;
|
||||
}
|
||||
|
||||
impl<'repo> TemplateLanguage<'repo> for CommitTemplateLanguage<'repo> {
|
||||
type Context = Commit;
|
||||
type Property = CommitTemplatePropertyKind<'repo>;
|
||||
|
@ -228,7 +232,7 @@ impl<'repo> IntoTemplateProperty<'repo, Commit> for CommitTemplatePropertyKind<'
|
|||
}
|
||||
|
||||
/// Table of functions that translate method call node of self type `T`.
|
||||
type CommitTemplateBuildMethodFnMap<'repo, T> =
|
||||
pub type CommitTemplateBuildMethodFnMap<'repo, T> =
|
||||
TemplateBuildMethodFnMap<'repo, CommitTemplateLanguage<'repo>, T>;
|
||||
|
||||
/// Symbol table of methods available in the commit template.
|
||||
|
@ -261,6 +265,28 @@ impl<'repo> CommitTemplateBuildFnTable<'repo> {
|
|||
shortest_id_prefix_methods: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, extension: CommitTemplateBuildFnTable<'repo>) {
|
||||
let CommitTemplateBuildFnTable {
|
||||
core,
|
||||
commit_methods,
|
||||
ref_name_methods,
|
||||
commit_or_change_id_methods,
|
||||
shortest_id_prefix_methods,
|
||||
} = extension;
|
||||
|
||||
self.core.merge(core);
|
||||
merge_fn_map(&mut self.commit_methods, commit_methods);
|
||||
merge_fn_map(&mut self.ref_name_methods, ref_name_methods);
|
||||
merge_fn_map(
|
||||
&mut self.commit_or_change_id_methods,
|
||||
commit_or_change_id_methods,
|
||||
);
|
||||
merge_fn_map(
|
||||
&mut self.shortest_id_prefix_methods,
|
||||
shortest_id_prefix_methods,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -805,14 +831,20 @@ pub fn parse<'repo>(
|
|||
repo: &'repo dyn Repo,
|
||||
workspace_id: &WorkspaceId,
|
||||
id_prefix_context: &'repo IdPrefixContext,
|
||||
extension: Option<&dyn CommitTemplateLanguageExtension>,
|
||||
template_text: &str,
|
||||
aliases_map: &TemplateAliasesMap,
|
||||
) -> TemplateParseResult<Box<dyn Template<Commit> + 'repo>> {
|
||||
let mut build_fn_table = CommitTemplateBuildFnTable::builtin();
|
||||
if let Some(extension) = extension {
|
||||
build_fn_table.merge(extension.build_fn_table());
|
||||
}
|
||||
|
||||
let language = CommitTemplateLanguage {
|
||||
repo,
|
||||
workspace_id: workspace_id.clone(),
|
||||
id_prefix_context,
|
||||
build_fn_table: CommitTemplateBuildFnTable::builtin(),
|
||||
build_fn_table,
|
||||
keyword_cache: CommitKeywordCache::default(),
|
||||
};
|
||||
let node = template_parser::parse(template_text, aliases_map)?;
|
||||
|
|
Loading…
Reference in a new issue