Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make node symbols templatable in the graphs #3313

Merged
merged 1 commit into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `ui.default-command` now accepts multiple string arguments, for more complex
default `jj` commands.

* Graph node symbols are now configurable via `ui.graph.default_node` and `ui.graph.elided_node`.
* Graph node symbols are now configurable via templates
* `templates.log_node`
* `templates.op_log_node`
* `templates.log_node_elided`


* `jj log` now includes synthetic nodes in the graph where some revisions were
elided.
Expand Down
19 changes: 16 additions & 3 deletions cli/src/cli_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -904,14 +904,19 @@ Set which revision the branch points to with `jj branch set {branch_name} -r <RE
&self,
template_text: &str,
) -> Result<Box<dyn Template<Commit> + '_>, CommandError> {
let language = CommitTemplateLanguage::new(
let language = self.commit_template_language()?;
self.parse_template(&language, template_text)
}

/// Creates commit template language environment for this workspace.
pub fn commit_template_language(&self) -> Result<CommitTemplateLanguage<'_>, CommandError> {
algmyr marked this conversation as resolved.
Show resolved Hide resolved
Ok(CommitTemplateLanguage::new(
self.repo().as_ref(),
self.workspace_id(),
self.revset_parse_context(),
self.id_prefix_context()?,
self.commit_template_extension.as_deref(),
);
self.parse_template(&language, template_text)
))
}

/// Template for one-line summary of a commit.
Expand Down Expand Up @@ -2309,6 +2314,14 @@ pub fn parse_args(
Ok((matches, args))
}

pub fn format_template<T>(ui: &Ui, arg: &T, template: &dyn Template<T>) -> String {
let mut output = vec![];
template
.format(arg, ui.new_formatter(&mut output).as_mut())
.expect("write() to vec backed formatter should never fail");
String::from_utf8(output).expect("template output should be utf-8 bytes")
}
algmyr marked this conversation as resolved.
Show resolved Hide resolved

/// CLI command builder and runner.
#[must_use]
pub struct CliRunner {
Expand Down
41 changes: 25 additions & 16 deletions cli/src/commands/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ use jj_lib::revset_graph::{
};
use tracing::instrument;

use crate::cli_util::{CommandHelper, LogContentFormat, RevisionArg};
use crate::cli_util::{format_template, CommandHelper, LogContentFormat, RevisionArg};
use crate::command_error::CommandError;
use crate::diff_util::{self, DiffFormatArgs};
use crate::generic_templater::GenericTemplateLanguage;
use crate::graphlog::{get_graphlog, Edge};
use crate::templater::Template;
use crate::ui::Ui;

/// Show revision history
Expand Down Expand Up @@ -94,34 +96,44 @@ pub(crate) fn cmd_log(
expression
};
let repo = workspace_command.repo();
let wc_commit_id = workspace_command.get_wc_commit_id();
let matcher = workspace_command.matcher_from_values(&args.paths)?;
let revset = workspace_command.evaluate_revset(revset_expression)?;

let store = repo.store();
let diff_formats =
diff_util::diff_formats_for_log(command.settings(), &args.diff_format, args.patch)?;

let template_string = match &args.template {
Some(value) => value.to_string(),
None => command.settings().config().get_string("templates.log")?,
};
let use_elided_nodes = command
.settings()
.config()
.get_bool("ui.log-synthetic-elided-nodes")?;
let template = workspace_command.parse_commit_template(&template_string)?;
let with_content_format = LogContentFormat::new(ui, command.settings())?;

let template;
let commit_node_template;
{
let language = workspace_command.commit_template_language()?;
let template_string = match &args.template {
Some(value) => value.to_string(),
None => command.settings().config().get_string("templates.log")?,
};
template = workspace_command.parse_template(&language, &template_string)?;
algmyr marked this conversation as resolved.
Show resolved Hide resolved
commit_node_template = workspace_command
.parse_template(&language, &command.settings().commit_node_template())?;
}

let elided_node_template = workspace_command.parse_template(
&GenericTemplateLanguage::new(),
&command.settings().elided_node_template(),
)?;

{
ui.request_pager();
let mut formatter = ui.stdout_formatter();
let formatter = formatter.as_mut();

if !args.no_graph {
let mut graph = get_graphlog(command.settings(), formatter.raw());
let default_node_symbol = command.settings().default_node_symbol();
let elided_node_symbol = command.settings().elided_node_symbol();
let forward_iter = TopoGroupedRevsetGraphIterator::new(revset.iter_graph());
let iter: Box<dyn Iterator<Item = _>> = if args.reversed {
Box::new(ReverseRevsetGraphIterator::new(forward_iter))
Expand Down Expand Up @@ -179,16 +191,12 @@ pub(crate) fn cmd_log(
&diff_formats,
)?;
}
let node_symbol = if Some(&key.0) == wc_commit_id {
"@"
} else {
&default_node_symbol
};

let node_symbol = format_template(ui, &commit, commit_node_template.as_ref());
graph.add_node(
&key,
&graphlog_edges,
node_symbol,
&node_symbol,
&String::from_utf8_lossy(&buffer),
)?;
for elided_target in elided_targets {
Expand All @@ -201,10 +209,11 @@ pub(crate) fn cmd_log(
|formatter| writeln!(formatter.labeled("elided"), "(elided revisions)"),
|| graph.width(&elided_key, &edges),
)?;
let node_symbol = format_template(ui, &(), elided_node_template.as_ref());
graph.add_node(
&elided_key,
&edges,
&elided_node_symbol,
&node_symbol,
&String::from_utf8_lossy(&buffer),
)?;
}
Expand Down
33 changes: 18 additions & 15 deletions cli/src/commands/obslog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ use jj_lib::matchers::EverythingMatcher;
use jj_lib::rewrite::rebase_to_dest_parent;
use tracing::instrument;

use crate::cli_util::{CommandHelper, LogContentFormat, RevisionArg, WorkspaceCommandHelper};
use crate::cli_util::{
format_template, CommandHelper, LogContentFormat, RevisionArg, WorkspaceCommandHelper,
};
use crate::command_error::CommandError;
use crate::diff_util::{self, DiffFormat, DiffFormatArgs};
use crate::formatter::Formatter;
Expand Down Expand Up @@ -63,18 +65,24 @@ pub(crate) fn cmd_obslog(
let workspace_command = command.workspace_helper(ui)?;

let start_commit = workspace_command.resolve_single_rev(&args.revision)?;
let wc_commit_id = workspace_command.get_wc_commit_id();

let diff_formats =
diff_util::diff_formats_for_log(command.settings(), &args.diff_format, args.patch)?;

let template_string = match &args.template {
Some(value) => value.to_string(),
None => command.settings().config().get_string("templates.log")?,
};
let template = workspace_command.parse_commit_template(&template_string)?;
let with_content_format = LogContentFormat::new(ui, command.settings())?;

let template;
let commit_node_template;
{
let language = workspace_command.commit_template_language()?;
let template_string = match &args.template {
Some(value) => value.to_string(),
None => command.settings().config().get_string("templates.log")?,
};
template = workspace_command.parse_template(&language, &template_string)?;
algmyr marked this conversation as resolved.
Show resolved Hide resolved
commit_node_template = workspace_command
.parse_template(&language, &command.settings().commit_node_template())?;
}

ui.request_pager();
let mut formatter = ui.stdout_formatter();
let formatter = formatter.as_mut();
Expand All @@ -90,7 +98,6 @@ pub(crate) fn cmd_obslog(
}
if !args.no_graph {
let mut graph = get_graphlog(command.settings(), formatter.raw());
let default_node_symbol = command.settings().default_node_symbol();
for commit in commits {
let mut edges = vec![];
for predecessor in &commit.predecessors() {
Expand All @@ -115,15 +122,11 @@ pub(crate) fn cmd_obslog(
&diff_formats,
)?;
}
let node_symbol = if Some(commit.id()) == wc_commit_id {
"@"
} else {
&default_node_symbol
};
let node_symbol = format_template(ui, &commit, commit_node_template.as_ref());
graph.add_node(
commit.id(),
&edges,
node_symbol,
&node_symbol,
&String::from_utf8_lossy(&buffer),
)?;
}
Expand Down
26 changes: 12 additions & 14 deletions cli/src/commands/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ use jj_lib::op_walk;
use jj_lib::operation::Operation;
use jj_lib::repo::Repo;

use crate::cli_util::{short_operation_hash, CommandHelper, LogContentFormat};
use crate::cli_util::{format_template, short_operation_hash, CommandHelper, LogContentFormat};
use crate::command_error::{user_error, user_error_with_hint, CommandError};
use crate::graphlog::{get_graphlog, Edge};
use crate::operation_templater::OperationTemplateLanguage;
use crate::templater::Template as _;
use crate::templater::Template;
use crate::ui::Ui;

/// Commands for working with the operation log
Expand Down Expand Up @@ -157,8 +157,11 @@ fn cmd_op_log(
[op] => Some(op.id()),
_ => None,
};
let with_content_format = LogContentFormat::new(ui, command.settings())?;

let template = {
let template;
let op_node_template;
{
let language = OperationTemplateLanguage::new(
repo_loader.op_store().root_operation_id(),
current_op_id,
Expand All @@ -168,24 +171,23 @@ fn cmd_op_log(
Some(value) => value.to_owned(),
None => command.settings().config().get_string("templates.op_log")?,
};
command.parse_template(ui, &language, &text)?
};
let with_content_format = LogContentFormat::new(ui, command.settings())?;
template = command.parse_template(ui, &language, &text)?;
op_node_template =
command.parse_template(ui, &language, &command.settings().op_node_template())?;
}

ui.request_pager();
let mut formatter = ui.stdout_formatter();
let formatter = formatter.as_mut();
let iter = op_walk::walk_ancestors(&head_ops).take(args.limit.unwrap_or(usize::MAX));
if !args.no_graph {
let mut graph = get_graphlog(command.settings(), formatter.raw());
let default_node_symbol = command.settings().default_node_symbol();
for op in iter {
let op = op?;
let mut edges = vec![];
for id in op.parent_ids() {
edges.push(Edge::Direct(id.clone()));
}
let is_current_op = Some(op.id()) == current_op_id;
let mut buffer = vec![];
with_content_format.write_graph_text(
ui.new_formatter(&mut buffer).as_mut(),
Expand All @@ -197,15 +199,11 @@ fn cmd_op_log(
if !buffer.ends_with(b"\n") {
buffer.push(b'\n');
}
let node_symbol = if is_current_op {
"@"
} else {
&default_node_symbol
};
let node_symbol = format_template(ui, &op, &op_node_template);
graph.add_node(
op.id(),
&edges,
node_symbol,
&node_symbol,
&String::from_utf8_lossy(&buffer),
)?;
}
Expand Down
8 changes: 0 additions & 8 deletions cli/src/config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,6 @@
"ascii-large"
],
"default": "curved"
},
"default_node": {
"type": "string",
"description": "The symbol used as the default symbol for nodes."
},
"elided_node": {
"type": "string",
"description": "The symbol used for elided nodes."
}
}
},
Expand Down
22 changes: 11 additions & 11 deletions cli/tests/test_log_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1409,7 +1409,7 @@ fn test_elided() {
}

#[test]
fn test_custom_symbols() {
fn test_log_with_custom_symbols() {
// Test that elided commits are shown as synthetic nodes.
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
Expand All @@ -1435,11 +1435,11 @@ fn test_custom_symbols() {
// Simple test with showing default and elided nodes.
test_env.add_config(concat!(
"ui.log-synthetic-elided-nodes = true\n",
"ui.graph.default_node = ''\n",
"ui.graph.elided_node = '🮀'",
"templates.log_node = 'if(current_working_copy, \"$\", if(root, \"\", \"\"))'\n",
"templates.log_node_elided = '\"🮀\"'",
));
insta::assert_snapshot!(get_log("@ | @- | description(initial)"), @r###"
@ merge
insta::assert_snapshot!(get_log("@ | @- | description(initial) | root()"), @r###"
$ merge
├─╮
│ ┝ side branch 2
│ │
Expand All @@ -1450,18 +1450,18 @@ fn test_custom_symbols() {
├─╯
┝ initial
~
"###);

// Simple test with showing default and elided nodes, ascii style.
test_env.add_config(concat!(
"ui.log-synthetic-elided-nodes = true\n",
"ui.graph.style = 'ascii'\n",
"ui.graph.default_node = '*'\n",
"ui.graph.elided_node = ':'",
"templates.log_node = 'if(current_working_copy, \"$\", if(root, \"^\", \"*\"))'\n",
"templates.log_node_elided = '\":\"'",
));
insta::assert_snapshot!(get_log("@ | @- | description(initial)"), @r###"
@ merge
insta::assert_snapshot!(get_log("@ | @- | description(initial) | root()"), @r###"
$ merge
|\
| * side branch 2
| |
Expand All @@ -1472,6 +1472,6 @@ fn test_custom_symbols() {
|/
* initial
|
~
^
"###);
}
Loading