just playing with tangled
at gvimdiff 3.9 kB view raw
1// Copyright 2020 The Jujutsu Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15use std::hash::Hash; 16use std::io; 17use std::io::Write; 18 19use jj_lib::config::ConfigGetError; 20use jj_lib::graph::GraphEdge; 21use jj_lib::graph::GraphEdgeType; 22use jj_lib::settings::UserSettings; 23use renderdag::Ancestor; 24use renderdag::GraphRowRenderer; 25use renderdag::Renderer; 26 27pub trait GraphLog<K: Clone + Eq + Hash> { 28 fn add_node( 29 &mut self, 30 id: &K, 31 edges: &[GraphEdge<K>], 32 node_symbol: &str, 33 text: &str, 34 ) -> io::Result<()>; 35 36 fn width(&self, id: &K, edges: &[GraphEdge<K>]) -> usize; 37} 38 39pub struct SaplingGraphLog<'writer, R> { 40 renderer: R, 41 writer: &'writer mut dyn Write, 42} 43 44fn convert_graph_edge_into_ancestor<K: Clone>(e: &GraphEdge<K>) -> Ancestor<K> { 45 match e.edge_type { 46 GraphEdgeType::Direct => Ancestor::Parent(e.target.clone()), 47 GraphEdgeType::Indirect => Ancestor::Ancestor(e.target.clone()), 48 GraphEdgeType::Missing => Ancestor::Anonymous, 49 } 50} 51 52impl<K, R> GraphLog<K> for SaplingGraphLog<'_, R> 53where 54 K: Clone + Eq + Hash, 55 R: Renderer<K, Output = String>, 56{ 57 fn add_node( 58 &mut self, 59 id: &K, 60 edges: &[GraphEdge<K>], 61 node_symbol: &str, 62 text: &str, 63 ) -> io::Result<()> { 64 let row = self.renderer.next_row( 65 id.clone(), 66 edges.iter().map(convert_graph_edge_into_ancestor).collect(), 67 node_symbol.into(), 68 text.into(), 69 ); 70 71 write!(self.writer, "{row}") 72 } 73 74 fn width(&self, id: &K, edges: &[GraphEdge<K>]) -> usize { 75 let parents = edges.iter().map(convert_graph_edge_into_ancestor).collect(); 76 let w: u64 = self.renderer.width(Some(id), Some(&parents)); 77 w.try_into().unwrap() 78 } 79} 80 81impl<'writer, R> SaplingGraphLog<'writer, R> { 82 pub fn create<K>( 83 renderer: R, 84 formatter: &'writer mut dyn Write, 85 ) -> Box<dyn GraphLog<K> + 'writer> 86 where 87 K: Clone + Eq + Hash + 'writer, 88 R: Renderer<K, Output = String> + 'writer, 89 { 90 Box::new(SaplingGraphLog { 91 renderer, 92 writer: formatter, 93 }) 94 } 95} 96 97#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize)] 98#[serde(rename_all(deserialize = "kebab-case"))] 99pub enum GraphStyle { 100 Ascii, 101 AsciiLarge, 102 Curved, 103 Square, 104} 105 106impl GraphStyle { 107 pub fn from_settings(settings: &UserSettings) -> Result<Self, ConfigGetError> { 108 settings.get("ui.graph.style") 109 } 110 111 pub fn is_ascii(self) -> bool { 112 match self { 113 GraphStyle::Ascii | GraphStyle::AsciiLarge => true, 114 GraphStyle::Curved | GraphStyle::Square => false, 115 } 116 } 117} 118 119pub fn get_graphlog<'a, K: Clone + Eq + Hash + 'a>( 120 style: GraphStyle, 121 formatter: &'a mut dyn Write, 122) -> Box<dyn GraphLog<K> + 'a> { 123 let builder = GraphRowRenderer::new().output().with_min_row_height(0); 124 match style { 125 GraphStyle::Ascii => SaplingGraphLog::create(builder.build_ascii(), formatter), 126 GraphStyle::AsciiLarge => SaplingGraphLog::create(builder.build_ascii_large(), formatter), 127 GraphStyle::Curved => SaplingGraphLog::create(builder.build_box_drawing(), formatter), 128 GraphStyle::Square => { 129 SaplingGraphLog::create(builder.build_box_drawing().with_square_glyphs(), formatter) 130 } 131 } 132}