Skip to content

Commit

Permalink
commit_templater: support extensions of the template language
Browse files Browse the repository at this point in the history
  • Loading branch information
torquestomp committed Mar 1, 2024
1 parent 1675554 commit 570fd29
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 4 deletions.
87 changes: 87 additions & 0 deletions cli/examples/custom-commit-templater/main.rs
Original file line number Diff line number Diff line change
@@ -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()
}
23 changes: 23 additions & 0 deletions cli/src/cli_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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>>,
Expand All @@ -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>>,
Expand All @@ -621,6 +624,7 @@ impl CommandHelper {
global_args,
settings,
layered_configs,
commit_template_extension,
maybe_workspace_loader,
store_factories,
working_copy_factories,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
)?;
Expand All @@ -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,
Expand Down Expand Up @@ -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,
)?;
Expand Down Expand Up @@ -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,
)
Expand Down Expand Up @@ -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,
)
Expand Down Expand Up @@ -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> {
Expand All @@ -2160,6 +2171,7 @@ fn parse_commit_summary_template<'a>(
repo,
workspace_id,
id_prefix_context,
extension,
&template_text,
aliases_map,
)?)
Expand Down Expand Up @@ -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>,
Expand All @@ -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![],
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
40 changes: 36 additions & 4 deletions cli/src/commit_templater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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>;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)?;
Expand Down

0 comments on commit 570fd29

Please sign in to comment.