diff --git a/cli/src/config-schema.json b/cli/src/config-schema.json index 6196575c9e7..eddb4369b5f 100644 --- a/cli/src/config-schema.json +++ b/cli/src/config-schema.json @@ -105,7 +105,6 @@ "style": { "description": "Style of connectors/markings used to render the graph. See https://github.com/martinvonz/jj/blob/main/docs/config.md#graph-style", "enum": [ - "legacy", "curved", "square", "ascii", diff --git a/cli/src/graphlog.rs b/cli/src/graphlog.rs index d57498bb74f..66d0a93cd64 100644 --- a/cli/src/graphlog.rs +++ b/cli/src/graphlog.rs @@ -13,8 +13,8 @@ // limitations under the License. use std::hash::Hash; +use std::io; use std::io::Write; -use std::{cmp, io}; use itertools::Itertools; use jj_lib::settings::UserSettings; @@ -138,7 +138,6 @@ pub fn get_graphlog<'a, K: Clone + Eq + Hash + 'a>( let builder = GraphRowRenderer::new().output().with_min_row_height(0); match settings.graph_style().as_str() { - "curved" => SaplingGraphLog::create(builder.build_box_drawing(), formatter, "◉"), "square" => SaplingGraphLog::create( builder.build_box_drawing().with_square_glyphs(), formatter, @@ -146,886 +145,6 @@ pub fn get_graphlog<'a, K: Clone + Eq + Hash + 'a>( ), "ascii" => SaplingGraphLog::create(builder.build_ascii(), formatter, "o"), "ascii-large" => SaplingGraphLog::create(builder.build_ascii_large(), formatter, "o"), - _ => Box::new(AsciiGraphDrawer::new(formatter)), - } -} - -pub struct AsciiGraphDrawer<'writer, K> { - writer: &'writer mut dyn Write, - edges: Vec>, - pending_text: Vec, -} - -impl<'writer, K> AsciiGraphDrawer<'writer, K> -where - K: Clone + Eq + Hash, -{ - pub fn new(writer: &'writer mut dyn Write) -> Self { - Self { - writer, - edges: Default::default(), - pending_text: Default::default(), - } - } -} - -impl<'writer, K: Clone + Eq + Hash> GraphLog for AsciiGraphDrawer<'writer, K> { - fn add_node( - &mut self, - id: &K, - edges: &[Edge], - node_symbol: &str, - text: &str, - ) -> io::Result<()> { - assert!(self.pending_text.is_empty()); - for line in text.split(|x| x == '\n') { - self.pending_text.push(line.into()); - } - if self.pending_text.last() == Some(&"".into()) { - self.pending_text.pop().unwrap(); - } - self.pending_text.reverse(); - - // Check if an existing edge should be terminated by the new node. If there - // is, draw the new node in the same column. Otherwise, insert it at the right. - let edge_index = if let Some(edge_index) = self.index_by_target(id) { - // This edge terminates in the node we're adding - - // If we're inserting a merge somewhere that's not the very right, the edges - // right of it will move further right, so we need to prepare by inserting rows - // of '\'. - if edges.len() > 2 && edge_index < self.edges.len() - 1 { - for i in 2..edges.len() { - for edge in self.edges.iter().take(edge_index + 1) { - AsciiGraphDrawer::straight_edge(&mut self.writer, edge)?; - } - for _ in 0..i - 2 { - write!(self.writer, " ")?; - } - for _ in edge_index + 1..self.edges.len() { - write!(self.writer, " \\")?; - } - writeln!(self.writer)?; - } - } - - self.edges.remove(edge_index); - edge_index - } else { - self.edges.len() - }; - - // Draw the edges to the left of the new node - for edge in self.edges.iter().take(edge_index) { - AsciiGraphDrawer::straight_edge(&mut self.writer, edge)?; - } - // Draw the new node - write!(self.writer, "{node_symbol}")?; - // If it's a merge of many nodes, draw a vertical line to the right - for _ in 3..edges.len() { - write!(self.writer, "--")?; - } - if edges.len() > 2 { - write!(self.writer, "-.")?; - } - write!(self.writer, " ")?; - // Draw the edges to the right of the new node - for edge in self.edges.iter().skip(edge_index) { - AsciiGraphDrawer::straight_edge(&mut self.writer, edge)?; - } - if edges.len() > 1 { - write!(self.writer, " ")?; - } - - self.maybe_write_pending_text()?; - - // Update the data model. - for (i, edge) in edges.iter().enumerate() { - self.edges.insert(edge_index + i, edge.clone()); - } - - // If it's a merge commit, insert a row of '\'. - if edges.len() >= 2 { - for edge in self.edges.iter().take(edge_index) { - AsciiGraphDrawer::straight_edge(&mut self.writer, edge)?; - } - AsciiGraphDrawer::straight_edge_no_space(&mut self.writer, &self.edges[edge_index])?; - for _ in edge_index + 1..self.edges.len() { - write!(self.writer, "\\ ")?; - } - write!(self.writer, " ")?; - self.maybe_write_pending_text()?; - } - - let pad_to_index = self.edges.len() + usize::from(edges.is_empty()); - // Close any edges to missing nodes. - for (i, edge) in edges.iter().enumerate().rev() { - if *edge == Edge::Missing { - self.close_missing_edge(edge_index + i, pad_to_index)?; - } - } - // If this was the last node in a chain, add a "/" for any edges to the right of - // it. - if edges.is_empty() && edge_index < self.edges.len() { - self.close_edge(edge_index, pad_to_index)?; - } - - // Merge new edges that share the same target. - let mut source_index = 1; - while source_index < self.edges.len() { - if let Edge::Present { target, .. } = &self.edges[source_index] { - if let Some(target_index) = self.index_by_target(target) { - // There already is an edge leading to the same target node. Mark that we - // want to merge the higher index into the lower index. - if source_index > target_index { - self.merge_edges(source_index, target_index, pad_to_index)?; - // Don't increment source_index. - continue; - } - } - } - source_index += 1; - } - - // Emit any remaining lines of text. - while !self.pending_text.is_empty() { - for edge in &self.edges { - AsciiGraphDrawer::straight_edge(&mut self.writer, edge)?; - } - for _ in self.edges.len()..pad_to_index { - write!(self.writer, " ")?; - } - self.maybe_write_pending_text()?; - } - - Ok(()) - } - - fn default_node_symbol(&self) -> &str { - "o" - } - - fn width(&self, id: &K, edges: &[Edge]) -> usize { - let orig = self.edges.len() - usize::from(self.index_by_target(id).is_some()); - let added = cmp::max(edges.len(), 1); - 2 * (orig + added) - } -} - -impl<'writer, K: Clone + Eq + Hash> AsciiGraphDrawer<'writer, K> { - fn index_by_target(&self, id: &K) -> Option { - for (i, edge) in self.edges.iter().enumerate() { - match edge { - Edge::Present { target, .. } if target == id => return Some(i), - _ => {} - } - } - None - } - - /// Not an instance method so the caller doesn't need mutable access to the - /// whole struct. - fn straight_edge(writer: &mut dyn Write, edge: &Edge) -> io::Result<()> { - AsciiGraphDrawer::straight_edge_no_space(writer, edge)?; - write!(writer, " ") - } - - /// Not an instance method so the caller doesn't need mutable access to the - /// whole struct. - fn straight_edge_no_space(writer: &mut dyn Write, edge: &Edge) -> io::Result<()> { - match edge { - Edge::Present { direct: true, .. } => { - write!(writer, "|")?; - } - Edge::Present { direct: false, .. } => { - write!(writer, ":")?; - } - Edge::Missing => { - write!(writer, "|")?; - } - } - Ok(()) - } - - fn merge_edges(&mut self, source: usize, target: usize, pad_to_index: usize) -> io::Result<()> { - assert!(target < source); - self.edges.remove(source); - for i in 0..target { - AsciiGraphDrawer::straight_edge(&mut self.writer, &self.edges[i])?; - } - if source == target + 1 { - // If we're merging exactly one step to the left, draw a '/' to join the lines. - AsciiGraphDrawer::straight_edge_no_space(&mut self.writer, &self.edges[target])?; - for _ in source..self.edges.len() + 1 { - write!(self.writer, "/ ")?; - } - write!(self.writer, " ")?; - for _ in self.edges.len() + 1..pad_to_index { - write!(self.writer, " ")?; - } - } else { - // If we're merging more than one step to the left, we need two rows: - // | |_|_|/ - // |/| | | - AsciiGraphDrawer::straight_edge(&mut self.writer, &self.edges[target])?; - for i in target + 1..source - 1 { - AsciiGraphDrawer::straight_edge_no_space(&mut self.writer, &self.edges[i])?; - write!(self.writer, "_")?; - } - AsciiGraphDrawer::straight_edge_no_space(&mut self.writer, &self.edges[source - 1])?; - for _ in source..self.edges.len() + 1 { - write!(self.writer, "/ ")?; - } - write!(self.writer, " ")?; - for _ in self.edges.len() + 1..pad_to_index { - write!(self.writer, " ")?; - } - self.maybe_write_pending_text()?; - - for i in 0..target { - AsciiGraphDrawer::straight_edge(&mut self.writer, &self.edges[i])?; - } - AsciiGraphDrawer::straight_edge_no_space(&mut self.writer, &self.edges[target])?; - write!(self.writer, "/")?; - for i in target + 1..self.edges.len() { - AsciiGraphDrawer::straight_edge(&mut self.writer, &self.edges[i])?; - } - for _ in self.edges.len()..pad_to_index { - write!(self.writer, " ")?; - } - } - self.maybe_write_pending_text()?; - - Ok(()) - } - - fn close_missing_edge(&mut self, source: usize, pad_to_index: usize) -> io::Result<()> { - self.edges.remove(source); - for i in 0..source { - AsciiGraphDrawer::straight_edge(&mut self.writer, &self.edges[i])?; - } - write!(self.writer, "~")?; - for _ in source..self.edges.len() { - write!(self.writer, "/ ")?; - } - write!(self.writer, " ")?; - for _ in self.edges.len() + 1..pad_to_index { - write!(self.writer, " ")?; - } - self.maybe_write_pending_text() - } - - fn close_edge(&mut self, source: usize, pad_to_index: usize) -> io::Result<()> { - for i in 0..source { - AsciiGraphDrawer::straight_edge(&mut self.writer, &self.edges[i])?; - } - write!(self.writer, " ")?; - for _ in source..self.edges.len() { - write!(self.writer, "/ ")?; - } - write!(self.writer, " ")?; - for _ in self.edges.len() + 1..pad_to_index { - write!(self.writer, " ")?; - } - self.maybe_write_pending_text() - } - - fn maybe_write_pending_text(&mut self) -> io::Result<()> { - if let Some(text) = self.pending_text.pop() { - write!(self.writer, "{text}")?; - } - writeln!(self.writer) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - /// Adapter to insert width calculation for each `.add_node()` - struct AsciiGraphDrawerWithWidths<'writer, K> { - inner: AsciiGraphDrawer<'writer, K>, - } - - impl<'writer, K: Clone + Eq + Hash> AsciiGraphDrawerWithWidths<'writer, K> { - fn new(writer: &'writer mut dyn Write) -> Self { - AsciiGraphDrawerWithWidths { - inner: AsciiGraphDrawer::new(writer), - } - } - - fn add_node( - &mut self, - id: &K, - edges: &[Edge], - node_symbol: &str, - text: &str, - ) -> io::Result<()> { - let width = self.inner.width(id, edges); - let text = format!("{text} [width={width}]"); - self.inner.add_node(id, edges, node_symbol, &text) - } - } - - #[test] - fn single_node() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&1, &[], "@", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - @ node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn long_description() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawer::new(&mut buffer); - graph.add_node(&2, &[Edge::direct(1)], "@", "many\nlines\nof\ntext\n")?; - graph.add_node(&1, &[], "o", "single line")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - @ many - | lines - | of - | text - o single line - "###); - - Ok(()) - } - - #[test] - fn long_description_blank_lines() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawer::new(&mut buffer); - graph.add_node( - &2, - &[Edge::direct(1)], - "@", - "\n\nmany\n\nlines\n\nof\n\ntext\n\n\n", - )?; - graph.add_node(&1, &[], "o", "single line")?; - - // A final newline is ignored but all other newlines are respected. - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - @ - | - | many - | - | lines - | - | of - | - | text - | - | - o single line - "###); - - Ok(()) - } - - #[test] - fn chain() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&3, &[Edge::direct(2)], "@", "node 3")?; - graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - @ node 3 [width=2] - o node 2 [width=2] - o node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn interleaved_chains() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&7, &[Edge::direct(5)], "o", "node 7")?; - graph.add_node(&6, &[Edge::direct(4)], "o", "node 6")?; - graph.add_node(&5, &[Edge::direct(3)], "o", "node 5")?; - graph.add_node(&4, &[Edge::direct(2)], "o", "node 4")?; - graph.add_node(&3, &[Edge::direct(1)], "@", "node 3")?; - graph.add_node(&2, &[], "o", "node 2")?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 7 [width=2] - | o node 6 [width=4] - o | node 5 [width=4] - | o node 4 [width=4] - @ | node 3 [width=4] - | o node 2 [width=4] - o node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn independent_nodes() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&3, &[Edge::missing()], "o", "node 3")?; - graph.add_node(&2, &[Edge::missing()], "o", "node 2")?; - graph.add_node(&1, &[Edge::missing()], "@", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 3 [width=2] - ~ - o node 2 [width=2] - ~ - @ node 1 [width=2] - ~ - "###); - - Ok(()) - } - - #[test] - fn left_chain_ends() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&4, &[Edge::direct(2)], "o", "node 4")?; - graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?; - graph.add_node(&2, &[Edge::missing()], "o", "node 2")?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 4 [width=2] - | o node 3 [width=4] - o | node 2 [width=4] - ~/ - o node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn left_chain_ends_with_no_missing_edge() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&4, &[Edge::direct(2)], "o", "node 4")?; - graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?; - graph.add_node(&2, &[], "o", "node 2\nmore\ntext")?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 4 [width=2] - | o node 3 [width=4] - o | node 2 - / more - | text [width=4] - o node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn right_chain_ends() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?; - graph.add_node(&3, &[Edge::direct(2)], "o", "node 3")?; - graph.add_node(&2, &[Edge::missing()], "o", "node 2")?; - graph.add_node(&1, &[], "o", "node 1\nmore\ntext")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 4 [width=2] - | o node 3 [width=4] - | o node 2 [width=4] - | ~ - o node 1 - more - text [width=2] - "###); - - Ok(()) - } - - #[test] - fn right_chain_ends_long_description() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawer::new(&mut buffer); - graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?; - graph.add_node( - &2, - &[Edge::missing()], - "o", - "node 2\nwith\nlong\ndescription", - )?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 3 - | o node 2 - | ~ with - | long - | description - o node 1 - "###); - - Ok(()) - } - - #[test] - fn fork_multiple() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&4, &[Edge::direct(1)], "@", "node 4")?; - graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?; - graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - @ node 4 [width=2] - | o node 3 [width=4] - |/ - | o node 2 [width=4] - |/ - o node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn fork_multiple_chains() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&10, &[Edge::direct(7)], "o", "node 10")?; - graph.add_node(&9, &[Edge::direct(6)], "o", "node 9")?; - graph.add_node(&8, &[Edge::direct(5)], "o", "node 8")?; - graph.add_node(&7, &[Edge::direct(4)], "o", "node 7")?; - graph.add_node(&6, &[Edge::direct(3)], "o", "node 6")?; - graph.add_node(&5, &[Edge::direct(2)], "o", "node 5")?; - graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?; - graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?; - graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 10 [width=2] - | o node 9 [width=4] - | | o node 8 [width=6] - o | | node 7 [width=6] - | o | node 6 [width=6] - | | o node 5 [width=6] - o | | node 4 [width=6] - | o | node 3 [width=6] - |/ / - | o node 2 [width=4] - |/ - o node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn cross_over() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&5, &[Edge::direct(1)], "o", "node 5")?; - graph.add_node(&4, &[Edge::direct(2)], "o", "node 4")?; - graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?; - graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 5 [width=2] - | o node 4 [width=4] - | | o node 3 [width=6] - | |/ - |/| - | o node 2 [width=4] - |/ - o node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn cross_over_multiple() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&7, &[Edge::direct(1)], "o", "node 7")?; - graph.add_node(&6, &[Edge::direct(3)], "o", "node 6")?; - graph.add_node(&5, &[Edge::direct(2)], "o", "node 5")?; - graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?; - graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?; - graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 7 [width=2] - | o node 6 [width=4] - | | o node 5 [width=6] - | | | o node 4 [width=8] - | |_|/ - |/| | - | o | node 3 [width=6] - |/ / - | o node 2 [width=4] - |/ - o node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn cross_over_new_on_left() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&6, &[Edge::direct(3)], "o", "node 6")?; - graph.add_node(&5, &[Edge::direct(2)], "o", "node 5")?; - graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?; - graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?; - graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 6 [width=2] - | o node 5 [width=4] - | | o node 4 [width=6] - o | | node 3 [width=6] - | |/ - |/| - | o node 2 [width=4] - |/ - o node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn merge_multiple() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node( - &5, - &[ - Edge::direct(1), - Edge::direct(2), - Edge::direct(3), - Edge::direct(4), - ], - "@", - "node 5\nmore\ntext", - )?; - graph.add_node(&4, &[Edge::missing()], "o", "node 4")?; - graph.add_node(&3, &[Edge::missing()], "o", "node 3")?; - graph.add_node(&2, &[Edge::missing()], "o", "node 2")?; - graph.add_node(&1, &[Edge::missing()], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - @---. node 5 - |\ \ \ more - | | | | text [width=8] - | | | o node 4 [width=8] - | | | ~ - | | o node 3 [width=6] - | | ~ - | o node 2 [width=4] - | ~ - o node 1 [width=2] - ~ - "###); - - Ok(()) - } - - #[test] - fn fork_merge_in_central_edge() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&8, &[Edge::direct(1)], "o", "node 8")?; - graph.add_node(&7, &[Edge::direct(5)], "o", "node 7")?; - graph.add_node( - &6, - &[Edge::direct(2)], - "o", - "node 6\nwith\nsome\nmore\nlines", - )?; - graph.add_node(&5, &[Edge::direct(4), Edge::direct(3)], "o", "node 5")?; - graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?; - graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?; - graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 8 [width=2] - | o node 7 [width=4] - | | o node 6 - | | | with - | | | some - | | | more - | | | lines [width=6] - | o | node 5 [width=8] - | |\ \ - | o | | node 4 [width=8] - |/ / / - | o | node 3 [width=6] - |/ / - | o node 2 [width=4] - |/ - o node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn fork_merge_multiple() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&6, &[Edge::direct(5)], "o", "node 6")?; - graph.add_node( - &5, - &[Edge::direct(2), Edge::direct(3), Edge::direct(4)], - "o", - "node 5", - )?; - graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?; - graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?; - graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 6 [width=2] - o-. node 5 [width=6] - |\ \ - | | o node 4 [width=6] - | o | node 3 [width=6] - | |/ - o | node 2 [width=4] - |/ - o node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn fork_merge_multiple_in_central_edge() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&10, &[Edge::direct(1)], "o", "node 10")?; - graph.add_node(&9, &[Edge::direct(7)], "o", "node 9")?; - graph.add_node(&8, &[Edge::direct(2)], "o", "node 8")?; - graph.add_node( - &7, - &[ - Edge::direct(6), - Edge::direct(5), - Edge::direct(4), - Edge::direct(3), - ], - "o", - "node 7", - )?; - graph.add_node(&6, &[Edge::direct(1)], "o", "node 6")?; - graph.add_node(&5, &[Edge::direct(1)], "o", "node 5")?; - graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?; - graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?; - graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 10 [width=2] - | o node 9 [width=4] - | | o node 8 [width=6] - | | \ - | | \ - | o---. | node 7 [width=12] - | |\ \ \ \ - | o | | | | node 6 [width=12] - |/ / / / / - | o | | | node 5 [width=10] - |/ / / / - | o | | node 4 [width=8] - |/ / / - | o | node 3 [width=6] - |/ / - | o node 2 [width=4] - |/ - o node 1 [width=2] - "###); - - Ok(()) - } - - #[test] - fn merge_multiple_missing_edges() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node( - &1, - &[ - Edge::missing(), - Edge::missing(), - Edge::missing(), - Edge::missing(), - ], - "@", - "node 1\nwith\nmany\nlines\nof\ntext", - )?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - @---. node 1 - |\ \ \ with - | | | ~ many - | | ~ lines - | ~ of - ~ text [width=8] - "###); - - Ok(()) - } - - #[test] - fn merge_missing_edges_and_fork() -> io::Result<()> { - let mut buffer = vec![]; - let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer); - graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?; - graph.add_node( - &2, - &[ - Edge::missing(), - Edge::indirect(1), - Edge::missing(), - Edge::indirect(1), - ], - "o", - "node 2\nwith\nmany\nlines\nof\ntext", - )?; - graph.add_node(&1, &[], "o", "node 1")?; - - insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###" - o node 3 [width=2] - | o---. node 2 - | |\ \ \ with - | | : ~/ many - | ~/ / lines - |/ / of - |/ text [width=10] - o node 1 [width=2] - "###); - - Ok(()) + _ => SaplingGraphLog::create(builder.build_box_drawing(), formatter, "◉"), } } diff --git a/cli/tests/test_log_command.rs b/cli/tests/test_log_command.rs index 0e43b0c12c1..32d1220a2bb 100644 --- a/cli/tests/test_log_command.rs +++ b/cli/tests/test_log_command.rs @@ -1310,28 +1310,6 @@ fn test_log_word_wrap() { 4 5 6 7 8 9 "###); - insta::assert_snapshot!( - render(&["log", "-T", template, "--config-toml=ui.graph.style='legacy'"], 9, true), - @r###" - @ 0 1 2 - |\ 3 4 5 - | | 6 7 8 - | | 9 - | o 0 1 2 - | | 3 4 5 - | | 6 7 8 - | | 9 - | o 0 1 2 - |/ 3 4 5 - | 6 7 8 - | 9 - o 0 1 2 3 - | 4 5 6 7 - | 8 9 - o 0 1 2 3 - 4 5 6 7 - 8 9 - "###); // Shouldn't panic with $COLUMNS < graph_width insta::assert_snapshot!(render(&["log", "-r@"], 0, true), @r###" diff --git a/docs/config.md b/docs/config.md index b42b664e1bf..f96da9bc522 100644 --- a/docs/config.md +++ b/docs/config.md @@ -218,8 +218,7 @@ revsets.log = "main@origin.." ### Graph style ```toml -# Possible values: "curved" (default), "square", "ascii", "ascii-large", -# "legacy" +# Possible values: "curved" (default), "square", "ascii", "ascii-large" ui.graph.style = "square" ```