mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-16 17:19:37 +00:00
commit_templater: extract runtime objects from templater.rs
This should reduce future merge conflicts around use statements.
This commit is contained in:
parent
c396b4cecf
commit
c5ddd14c13
2 changed files with 230 additions and 230 deletions
|
@ -12,21 +12,26 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use jujutsu_lib::backend::{Signature, Timestamp};
|
||||
use std::cmp::max;
|
||||
use std::io;
|
||||
|
||||
use itertools::Itertools as _;
|
||||
use jujutsu_lib::backend::{ChangeId, CommitId, ObjectId as _, Signature, Timestamp};
|
||||
use jujutsu_lib::commit::Commit;
|
||||
use jujutsu_lib::hex_util::to_reverse_hex;
|
||||
use jujutsu_lib::op_store::WorkspaceId;
|
||||
use jujutsu_lib::repo::Repo;
|
||||
use jujutsu_lib::rewrite;
|
||||
|
||||
use crate::cli_util;
|
||||
use crate::formatter::Formatter;
|
||||
use crate::template_parser::{
|
||||
self, CoreTemplatePropertyKind, FunctionCallNode, IntoTemplateProperty, TemplateAliasesMap,
|
||||
TemplateLanguage, TemplateParseError, TemplateParseResult,
|
||||
};
|
||||
use crate::templater::{
|
||||
BranchProperty, CommitOrChangeId, FormattablePropertyTemplate, GitHeadProperty,
|
||||
GitRefsProperty, PlainTextFormattedProperty, ShortestIdPrefix, TagProperty, Template,
|
||||
TemplateProperty, TemplatePropertyFn, WorkingCopiesProperty,
|
||||
FormattablePropertyTemplate, PlainTextFormattedProperty, Template, TemplateProperty,
|
||||
TemplatePropertyFn,
|
||||
};
|
||||
|
||||
struct CommitTemplateLanguage<'a, 'b> {
|
||||
|
@ -201,6 +206,210 @@ fn build_commit_keyword<'a>(
|
|||
Ok(property)
|
||||
}
|
||||
|
||||
struct WorkingCopiesProperty<'a> {
|
||||
pub repo: &'a dyn Repo,
|
||||
}
|
||||
|
||||
impl TemplateProperty<Commit> for WorkingCopiesProperty<'_> {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Commit) -> Self::Output {
|
||||
let wc_commit_ids = self.repo.view().wc_commit_ids();
|
||||
if wc_commit_ids.len() <= 1 {
|
||||
return "".to_string();
|
||||
}
|
||||
let mut names = vec![];
|
||||
for (workspace_id, wc_commit_id) in wc_commit_ids.iter().sorted() {
|
||||
if wc_commit_id == context.id() {
|
||||
names.push(format!("{}@", workspace_id.as_str()));
|
||||
}
|
||||
}
|
||||
names.join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
struct BranchProperty<'a> {
|
||||
pub repo: &'a dyn Repo,
|
||||
}
|
||||
|
||||
impl TemplateProperty<Commit> for BranchProperty<'_> {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Commit) -> Self::Output {
|
||||
let mut names = vec![];
|
||||
for (branch_name, branch_target) in self.repo.view().branches() {
|
||||
let local_target = branch_target.local_target.as_ref();
|
||||
if let Some(local_target) = local_target {
|
||||
if local_target.has_add(context.id()) {
|
||||
if local_target.is_conflict() {
|
||||
names.push(format!("{branch_name}??"));
|
||||
} else if branch_target
|
||||
.remote_targets
|
||||
.values()
|
||||
.any(|remote_target| remote_target != local_target)
|
||||
{
|
||||
names.push(format!("{branch_name}*"));
|
||||
} else {
|
||||
names.push(branch_name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (remote_name, remote_target) in &branch_target.remote_targets {
|
||||
if Some(remote_target) != local_target && remote_target.has_add(context.id()) {
|
||||
if remote_target.is_conflict() {
|
||||
names.push(format!("{branch_name}@{remote_name}?"));
|
||||
} else {
|
||||
names.push(format!("{branch_name}@{remote_name}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
names.join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
struct TagProperty<'a> {
|
||||
pub repo: &'a dyn Repo,
|
||||
}
|
||||
|
||||
impl TemplateProperty<Commit> for TagProperty<'_> {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Commit) -> Self::Output {
|
||||
let mut names = vec![];
|
||||
for (tag_name, target) in self.repo.view().tags() {
|
||||
if target.has_add(context.id()) {
|
||||
if target.is_conflict() {
|
||||
names.push(format!("{tag_name}?"));
|
||||
} else {
|
||||
names.push(tag_name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
names.join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
struct GitRefsProperty<'a> {
|
||||
pub repo: &'a dyn Repo,
|
||||
}
|
||||
|
||||
impl TemplateProperty<Commit> for GitRefsProperty<'_> {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Commit) -> Self::Output {
|
||||
// TODO: We should keep a map from commit to ref names so we don't have to walk
|
||||
// all refs here.
|
||||
let mut names = vec![];
|
||||
for (name, target) in self.repo.view().git_refs() {
|
||||
if target.has_add(context.id()) {
|
||||
if target.is_conflict() {
|
||||
names.push(format!("{name}?"));
|
||||
} else {
|
||||
names.push(name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
names.join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
struct GitHeadProperty<'a> {
|
||||
repo: &'a dyn Repo,
|
||||
}
|
||||
|
||||
impl<'a> GitHeadProperty<'a> {
|
||||
pub fn new(repo: &'a dyn Repo) -> Self {
|
||||
Self { repo }
|
||||
}
|
||||
}
|
||||
|
||||
impl TemplateProperty<Commit> for GitHeadProperty<'_> {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Commit) -> String {
|
||||
match self.repo.view().git_head() {
|
||||
Some(ref_target) if ref_target.has_add(context.id()) => {
|
||||
if ref_target.is_conflict() {
|
||||
"HEAD@git?".to_string()
|
||||
} else {
|
||||
"HEAD@git".to_string()
|
||||
}
|
||||
}
|
||||
_ => "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type-erased `CommitId`/`ChangeId`.
|
||||
#[derive(Clone)]
|
||||
struct CommitOrChangeId<'a> {
|
||||
repo: &'a dyn Repo,
|
||||
id_bytes: Vec<u8>,
|
||||
is_commit_id: bool,
|
||||
}
|
||||
|
||||
impl<'a> CommitOrChangeId<'a> {
|
||||
pub fn commit_id(repo: &'a dyn Repo, id: &CommitId) -> Self {
|
||||
CommitOrChangeId {
|
||||
repo,
|
||||
id_bytes: id.to_bytes(),
|
||||
is_commit_id: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_id(repo: &'a dyn Repo, id: &ChangeId) -> Self {
|
||||
CommitOrChangeId {
|
||||
repo,
|
||||
id_bytes: id.to_bytes(),
|
||||
is_commit_id: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hex(&self) -> String {
|
||||
if self.is_commit_id {
|
||||
hex::encode(&self.id_bytes)
|
||||
} else {
|
||||
// TODO: We can avoid the unwrap() and make this more efficient by converting
|
||||
// straight from bytes.
|
||||
to_reverse_hex(&hex::encode(&self.id_bytes)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn short(&self, total_len: usize) -> String {
|
||||
let mut hex = self.hex();
|
||||
hex.truncate(total_len);
|
||||
hex
|
||||
}
|
||||
|
||||
/// The length of the id printed will be the maximum of `total_len` and the
|
||||
/// length of the shortest unique prefix
|
||||
pub fn shortest(&self, total_len: usize) -> ShortestIdPrefix {
|
||||
let mut hex = self.hex();
|
||||
let prefix_len = if self.is_commit_id {
|
||||
self.repo
|
||||
.index()
|
||||
.shortest_unique_commit_id_prefix_len(&CommitId::from_bytes(&self.id_bytes))
|
||||
} else {
|
||||
self.repo
|
||||
.shortest_unique_change_id_prefix_len(&ChangeId::from_bytes(&self.id_bytes))
|
||||
};
|
||||
hex.truncate(max(prefix_len, total_len));
|
||||
let rest = hex.split_off(prefix_len);
|
||||
ShortestIdPrefix { prefix: hex, rest }
|
||||
}
|
||||
}
|
||||
|
||||
impl Template<()> for CommitOrChangeId<'_> {
|
||||
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
||||
formatter.write_str(&self.hex())
|
||||
}
|
||||
|
||||
fn has_content(&self, _: &()) -> bool {
|
||||
!self.id_bytes.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
fn build_commit_or_change_id_method<'a>(
|
||||
language: &CommitTemplateLanguage<'a, '_>,
|
||||
self_property: impl TemplateProperty<Commit, Output = CommitOrChangeId<'a>> + 'a,
|
||||
|
@ -247,6 +456,22 @@ fn build_commit_or_change_id_method<'a>(
|
|||
Ok(property)
|
||||
}
|
||||
|
||||
struct ShortestIdPrefix {
|
||||
pub prefix: String,
|
||||
pub rest: String,
|
||||
}
|
||||
|
||||
impl Template<()> for ShortestIdPrefix {
|
||||
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
||||
formatter.with_label("prefix", |fmt| fmt.write_str(&self.prefix))?;
|
||||
formatter.with_label("rest", |fmt| fmt.write_str(&self.rest))
|
||||
}
|
||||
|
||||
fn has_content(&self, _: &()) -> bool {
|
||||
!self.prefix.is_empty() || !self.rest.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
fn build_shortest_id_prefix_method<'a>(
|
||||
language: &CommitTemplateLanguage<'a, '_>,
|
||||
self_property: impl TemplateProperty<Commit, Output = ShortestIdPrefix> + 'a,
|
||||
|
|
227
src/templater.rs
227
src/templater.rs
|
@ -12,14 +12,9 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::cmp::max;
|
||||
use std::io;
|
||||
|
||||
use itertools::Itertools;
|
||||
use jujutsu_lib::backend::{ChangeId, CommitId, ObjectId, Signature, Timestamp};
|
||||
use jujutsu_lib::commit::Commit;
|
||||
use jujutsu_lib::hex_util::to_reverse_hex;
|
||||
use jujutsu_lib::repo::Repo;
|
||||
use jujutsu_lib::backend::{Signature, Timestamp};
|
||||
|
||||
use crate::formatter::{Formatter, PlainTextFormatter};
|
||||
use crate::time_util;
|
||||
|
@ -328,141 +323,6 @@ impl<C, T: Template<C>> TemplateProperty<C> for PlainTextFormattedProperty<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct WorkingCopiesProperty<'a> {
|
||||
pub repo: &'a dyn Repo,
|
||||
}
|
||||
|
||||
impl TemplateProperty<Commit> for WorkingCopiesProperty<'_> {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Commit) -> Self::Output {
|
||||
let wc_commit_ids = self.repo.view().wc_commit_ids();
|
||||
if wc_commit_ids.len() <= 1 {
|
||||
return "".to_string();
|
||||
}
|
||||
let mut names = vec![];
|
||||
for (workspace_id, wc_commit_id) in wc_commit_ids.iter().sorted() {
|
||||
if wc_commit_id == context.id() {
|
||||
names.push(format!("{}@", workspace_id.as_str()));
|
||||
}
|
||||
}
|
||||
names.join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BranchProperty<'a> {
|
||||
pub repo: &'a dyn Repo,
|
||||
}
|
||||
|
||||
impl TemplateProperty<Commit> for BranchProperty<'_> {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Commit) -> Self::Output {
|
||||
let mut names = vec![];
|
||||
for (branch_name, branch_target) in self.repo.view().branches() {
|
||||
let local_target = branch_target.local_target.as_ref();
|
||||
if let Some(local_target) = local_target {
|
||||
if local_target.has_add(context.id()) {
|
||||
if local_target.is_conflict() {
|
||||
names.push(format!("{branch_name}??"));
|
||||
} else if branch_target
|
||||
.remote_targets
|
||||
.values()
|
||||
.any(|remote_target| remote_target != local_target)
|
||||
{
|
||||
names.push(format!("{branch_name}*"));
|
||||
} else {
|
||||
names.push(branch_name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (remote_name, remote_target) in &branch_target.remote_targets {
|
||||
if Some(remote_target) != local_target && remote_target.has_add(context.id()) {
|
||||
if remote_target.is_conflict() {
|
||||
names.push(format!("{branch_name}@{remote_name}?"));
|
||||
} else {
|
||||
names.push(format!("{branch_name}@{remote_name}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
names.join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TagProperty<'a> {
|
||||
pub repo: &'a dyn Repo,
|
||||
}
|
||||
|
||||
impl TemplateProperty<Commit> for TagProperty<'_> {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Commit) -> Self::Output {
|
||||
let mut names = vec![];
|
||||
for (tag_name, target) in self.repo.view().tags() {
|
||||
if target.has_add(context.id()) {
|
||||
if target.is_conflict() {
|
||||
names.push(format!("{tag_name}?"));
|
||||
} else {
|
||||
names.push(tag_name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
names.join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GitRefsProperty<'a> {
|
||||
pub repo: &'a dyn Repo,
|
||||
}
|
||||
|
||||
impl TemplateProperty<Commit> for GitRefsProperty<'_> {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Commit) -> Self::Output {
|
||||
// TODO: We should keep a map from commit to ref names so we don't have to walk
|
||||
// all refs here.
|
||||
let mut names = vec![];
|
||||
for (name, target) in self.repo.view().git_refs() {
|
||||
if target.has_add(context.id()) {
|
||||
if target.is_conflict() {
|
||||
names.push(format!("{name}?"));
|
||||
} else {
|
||||
names.push(name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
names.join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GitHeadProperty<'a> {
|
||||
repo: &'a dyn Repo,
|
||||
}
|
||||
|
||||
impl<'a> GitHeadProperty<'a> {
|
||||
pub fn new(repo: &'a dyn Repo) -> Self {
|
||||
Self { repo }
|
||||
}
|
||||
}
|
||||
|
||||
impl TemplateProperty<Commit> for GitHeadProperty<'_> {
|
||||
type Output = String;
|
||||
|
||||
fn extract(&self, context: &Commit) -> String {
|
||||
match self.repo.view().git_head() {
|
||||
Some(ref_target) if ref_target.has_add(context.id()) => {
|
||||
if ref_target.is_conflict() {
|
||||
"HEAD@git?".to_string()
|
||||
} else {
|
||||
"HEAD@git".to_string()
|
||||
}
|
||||
}
|
||||
_ => "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConditionalTemplate<P, T, U> {
|
||||
pub condition: P,
|
||||
pub true_template: T,
|
||||
|
@ -538,88 +398,3 @@ where
|
|||
(self.function)(self.property.extract(context))
|
||||
}
|
||||
}
|
||||
|
||||
/// Type-erased `CommitId`/`ChangeId`.
|
||||
#[derive(Clone)]
|
||||
pub struct CommitOrChangeId<'a> {
|
||||
repo: &'a dyn Repo,
|
||||
id_bytes: Vec<u8>,
|
||||
is_commit_id: bool,
|
||||
}
|
||||
|
||||
impl<'a> CommitOrChangeId<'a> {
|
||||
pub fn commit_id(repo: &'a dyn Repo, id: &CommitId) -> Self {
|
||||
CommitOrChangeId {
|
||||
repo,
|
||||
id_bytes: id.to_bytes(),
|
||||
is_commit_id: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_id(repo: &'a dyn Repo, id: &ChangeId) -> Self {
|
||||
CommitOrChangeId {
|
||||
repo,
|
||||
id_bytes: id.to_bytes(),
|
||||
is_commit_id: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hex(&self) -> String {
|
||||
if self.is_commit_id {
|
||||
hex::encode(&self.id_bytes)
|
||||
} else {
|
||||
// TODO: We can avoid the unwrap() and make this more efficient by converting
|
||||
// straight from bytes.
|
||||
to_reverse_hex(&hex::encode(&self.id_bytes)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn short(&self, total_len: usize) -> String {
|
||||
let mut hex = self.hex();
|
||||
hex.truncate(total_len);
|
||||
hex
|
||||
}
|
||||
|
||||
/// The length of the id printed will be the maximum of `total_len` and the
|
||||
/// length of the shortest unique prefix
|
||||
pub fn shortest(&self, total_len: usize) -> ShortestIdPrefix {
|
||||
let mut hex = self.hex();
|
||||
let prefix_len = if self.is_commit_id {
|
||||
self.repo
|
||||
.index()
|
||||
.shortest_unique_commit_id_prefix_len(&CommitId::from_bytes(&self.id_bytes))
|
||||
} else {
|
||||
self.repo
|
||||
.shortest_unique_change_id_prefix_len(&ChangeId::from_bytes(&self.id_bytes))
|
||||
};
|
||||
hex.truncate(max(prefix_len, total_len));
|
||||
let rest = hex.split_off(prefix_len);
|
||||
ShortestIdPrefix { prefix: hex, rest }
|
||||
}
|
||||
}
|
||||
|
||||
impl Template<()> for CommitOrChangeId<'_> {
|
||||
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
||||
formatter.write_str(&self.hex())
|
||||
}
|
||||
|
||||
fn has_content(&self, _: &()) -> bool {
|
||||
!self.id_bytes.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ShortestIdPrefix {
|
||||
pub prefix: String,
|
||||
pub rest: String,
|
||||
}
|
||||
|
||||
impl Template<()> for ShortestIdPrefix {
|
||||
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
|
||||
formatter.with_label("prefix", |fmt| fmt.write_str(&self.prefix))?;
|
||||
formatter.with_label("rest", |fmt| fmt.write_str(&self.rest))
|
||||
}
|
||||
|
||||
fn has_content(&self, _: &()) -> bool {
|
||||
!self.prefix.is_empty() || !self.rest.is_empty()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue