termdiff/themes/theme.rs
1use std::{borrow::Cow, fmt::Debug};
2
3/// A [`Theme`] for customizing the appearance of diffs
4///
5/// This trait allows you to control how diffs are displayed without having
6/// to parse the diff output yourself. You can customize prefixes, highlighting,
7/// and formatting for different types of changes.
8///
9/// # Implementing a Custom Theme
10///
11/// To create a custom theme, you must implement at minimum:
12/// - `equal_prefix`: Prefix for unchanged lines
13/// - `delete_prefix`: Prefix for removed lines
14/// - `insert_prefix`: Prefix for added lines
15/// - `header`: Header text displayed at the top of the diff
16///
17/// All other methods have default implementations that you can override as needed.
18///
19/// # Example
20///
21/// ```rust
22/// use std::borrow::Cow;
23/// use termdiff::Theme;
24///
25/// #[derive(Debug)]
26/// struct MyCustomTheme {}
27///
28/// impl Theme for MyCustomTheme {
29/// // Required methods
30/// fn equal_prefix<'this>(&self) -> Cow<'this, str> {
31/// " ".into() // Space for unchanged lines
32/// }
33///
34/// fn delete_prefix<'this>(&self) -> Cow<'this, str> {
35/// "[-]".into() // Custom prefix for removed lines
36/// }
37///
38/// fn insert_prefix<'this>(&self) -> Cow<'this, str> {
39/// "[+]".into() // Custom prefix for added lines
40/// }
41///
42/// fn header<'this>(&self) -> Cow<'this, str> {
43/// "=== DIFF RESULTS ===\n".into() // Custom header
44/// }
45///
46/// // Optional overrides for customizing content formatting
47/// fn delete_content<'this>(&self, input: &'this str) -> Cow<'this, str> {
48/// format!("REMOVED: {}", input).into() // Custom formatting for removed content
49/// }
50///
51/// fn insert_line<'this>(&self, input: &'this str) -> Cow<'this, str> {
52/// format!("ADDED: {}", input).into() // Custom formatting for added content
53/// }
54/// }
55/// ```
56pub trait Theme: Debug {
57 /// How to format the text when highlighting specific changes in inserted lines
58 ///
59 /// This is used to highlight the specific parts of text that have changed within
60 /// an inserted line. By default, it returns the input unchanged.
61 ///
62 /// # Example
63 ///
64 /// ```
65 /// # use std::borrow::Cow;
66 /// # use termdiff::Theme;
67 /// # #[derive(Debug)]
68 /// # struct MyTheme;
69 /// # impl Theme for MyTheme {
70 /// # fn equal_prefix<'a>(&self) -> Cow<'a, str> { " ".into() }
71 /// # fn delete_prefix<'a>(&self) -> Cow<'a, str> { "-".into() }
72 /// # fn insert_prefix<'a>(&self) -> Cow<'a, str> { "+".into() }
73 /// # fn header<'a>(&self) -> Cow<'a, str> { "".into() }
74 /// fn highlight_insert<'this>(&self, input: &'this str) -> Cow<'this, str> {
75 /// format!("**{}**", input).into() // Bold the inserted text
76 /// }
77 /// # }
78 /// ```
79 fn highlight_insert<'this>(&self, input: &'this str) -> Cow<'this, str> {
80 input.into()
81 }
82
83 /// How to format the text when highlighting specific changes in deleted lines
84 ///
85 /// This is used to highlight the specific parts of text that have changed within
86 /// a deleted line. By default, it returns the input unchanged.
87 fn highlight_delete<'this>(&self, input: &'this str) -> Cow<'this, str> {
88 input.into()
89 }
90
91 /// How to format unchanged content
92 ///
93 /// This method is called for content that exists in both the old and new text.
94 /// By default, it returns the input unchanged.
95 fn equal_content<'this>(&self, input: &'this str) -> Cow<'this, str> {
96 input.into()
97 }
98
99 /// How to format content that is being removed
100 ///
101 /// This method is called for content that exists only in the old text.
102 /// By default, it returns the input unchanged.
103 fn delete_content<'this>(&self, input: &'this str) -> Cow<'this, str> {
104 input.into()
105 }
106
107 /// The prefix to display before lines that are unchanged
108 ///
109 /// This is typically a space or other character that indicates the line is unchanged.
110 /// This method is required for all theme implementations.
111 fn equal_prefix<'this>(&self) -> Cow<'this, str>;
112
113 /// The prefix to display before lines that are being removed
114 ///
115 /// This is typically a character like '-' that indicates the line is being removed.
116 /// This method is required for all theme implementations.
117 fn delete_prefix<'this>(&self) -> Cow<'this, str>;
118
119 /// How to format content that is being added
120 ///
121 /// This method is called for content that exists only in the new text.
122 /// By default, it returns the input unchanged.
123 fn insert_line<'this>(&self, input: &'this str) -> Cow<'this, str> {
124 input.into()
125 }
126
127 /// The prefix to display before lines that are being added
128 ///
129 /// This is typically a character like '+' that indicates the line is being added.
130 /// This method is required for all theme implementations.
131 fn insert_prefix<'this>(&self) -> Cow<'this, str>;
132
133 /// The string to append when a diff line doesn't end with a newline
134 ///
135 /// By default, this adds a newline character.
136 fn line_end<'this>(&self) -> Cow<'this, str> {
137 "\n".into()
138 }
139
140 /// The marker to show when one string has a trailing newline and the other doesn't
141 ///
142 /// When one of the two strings ends with a newline and the other doesn't,
143 /// this character is inserted before the newline to make the difference visible.
144 /// By default, this uses the Unicode character '␊' (U+240A).
145 fn trailing_lf_marker<'this>(&self) -> Cow<'this, str> {
146 "␊".into()
147 }
148
149 /// The header text to display at the top of the diff
150 ///
151 /// This is typically a line explaining the diff format.
152 /// This method is required for all theme implementations.
153 fn header<'this>(&self) -> Cow<'this, str>;
154}