1use super::{Hunk, Line, Patch, NO_NEWLINE_AT_EOF};
2use nu_ansi_term::{Color, Style};
3use std::{
4 fmt::{Display, Formatter, Result},
5 io,
6};
7
8#[derive(Debug)]
10pub struct PatchFormatter {
11 with_color: bool,
12 with_missing_newline_message: bool,
13 suppress_blank_empty: bool,
14
15 context: Style,
16 delete: Style,
17 insert: Style,
18 hunk_header: Style,
19 patch_header: Style,
20 function_context: Style,
21}
22
23impl PatchFormatter {
24 pub fn new() -> Self {
26 Self {
27 with_color: false,
28 with_missing_newline_message: true,
29
30 suppress_blank_empty: true,
33
34 context: Style::new(),
35 delete: Color::Red.normal(),
36 insert: Color::Green.normal(),
37 hunk_header: Color::Cyan.normal(),
38 patch_header: Style::new().bold(),
39 function_context: Style::new(),
40 }
41 }
42
43 pub fn with_color(mut self) -> Self {
45 self.with_color = true;
46 self
47 }
48
49 pub fn missing_newline_message(mut self, enable: bool) -> Self {
58 self.with_missing_newline_message = enable;
59 self
60 }
61
62 pub fn suppress_blank_empty(mut self, enable: bool) -> Self {
72 self.suppress_blank_empty = enable;
73 self
74 }
75
76 pub fn fmt_patch<'a>(&'a self, patch: &'a Patch<'a, str>) -> impl Display + 'a {
78 PatchDisplay { f: self, patch }
79 }
80
81 pub fn write_patch_into<T: ToOwned + AsRef<[u8]> + ?Sized, W: io::Write>(
82 &self,
83 patch: &Patch<'_, T>,
84 w: W,
85 ) -> io::Result<()> {
86 PatchDisplay { f: self, patch }.write_into(w)
87 }
88
89 fn fmt_hunk<'a>(&'a self, hunk: &'a Hunk<'a, str>) -> impl Display + 'a {
90 HunkDisplay { f: self, hunk }
91 }
92
93 fn write_hunk_into<T: AsRef<[u8]> + ?Sized, W: io::Write>(
94 &self,
95 hunk: &Hunk<'_, T>,
96 w: W,
97 ) -> io::Result<()> {
98 HunkDisplay { f: self, hunk }.write_into(w)
99 }
100
101 fn fmt_line<'a>(&'a self, line: &'a Line<'a, str>) -> impl Display + 'a {
102 LineDisplay { f: self, line }
103 }
104
105 fn write_line_into<T: AsRef<[u8]> + ?Sized, W: io::Write>(
106 &self,
107 line: &Line<'_, T>,
108 w: W,
109 ) -> io::Result<()> {
110 LineDisplay { f: self, line }.write_into(w)
111 }
112}
113
114impl Default for PatchFormatter {
115 fn default() -> Self {
116 Self::new()
117 }
118}
119
120struct PatchDisplay<'a, T: ToOwned + ?Sized> {
121 f: &'a PatchFormatter,
122 patch: &'a Patch<'a, T>,
123}
124
125impl<T: ToOwned + AsRef<[u8]> + ?Sized> PatchDisplay<'_, T> {
126 fn write_into<W: io::Write>(&self, mut w: W) -> io::Result<()> {
127 if self.patch.original.is_some() || self.patch.modified.is_some() {
128 if self.f.with_color {
129 write!(w, "{}", self.f.patch_header.prefix())?;
130 }
131 if let Some(original) = &self.patch.original {
132 write!(w, "--- ")?;
133 original.write_into(&mut w)?;
134 writeln!(w)?;
135 }
136 if let Some(modified) = &self.patch.modified {
137 write!(w, "+++ ")?;
138 modified.write_into(&mut w)?;
139 writeln!(w)?;
140 }
141 if self.f.with_color {
142 write!(w, "{}", self.f.patch_header.suffix())?;
143 }
144 }
145
146 for hunk in &self.patch.hunks {
147 self.f.write_hunk_into(hunk, &mut w)?;
148 }
149
150 Ok(())
151 }
152}
153
154impl Display for PatchDisplay<'_, str> {
155 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
156 if self.patch.original.is_some() || self.patch.modified.is_some() {
157 if self.f.with_color {
158 write!(f, "{}", self.f.patch_header.prefix())?;
159 }
160 if let Some(original) = &self.patch.original {
161 writeln!(f, "--- {}", original)?;
162 }
163 if let Some(modified) = &self.patch.modified {
164 writeln!(f, "+++ {}", modified)?;
165 }
166 if self.f.with_color {
167 write!(f, "{}", self.f.patch_header.suffix())?;
168 }
169 }
170
171 for hunk in &self.patch.hunks {
172 write!(f, "{}", self.f.fmt_hunk(hunk))?;
173 }
174
175 Ok(())
176 }
177}
178
179struct HunkDisplay<'a, T: ?Sized> {
180 f: &'a PatchFormatter,
181 hunk: &'a Hunk<'a, T>,
182}
183
184impl<T: AsRef<[u8]> + ?Sized> HunkDisplay<'_, T> {
185 fn write_into<W: io::Write>(&self, mut w: W) -> io::Result<()> {
186 if self.f.with_color {
187 write!(w, "{}", self.f.hunk_header.prefix())?;
188 }
189 write!(w, "@@ -{} +{} @@", self.hunk.old_range, self.hunk.new_range)?;
190 if self.f.with_color {
191 write!(w, "{}", self.f.hunk_header.suffix())?;
192 }
193
194 if let Some(ctx) = self.hunk.function_context {
195 write!(w, " ")?;
196 if self.f.with_color {
197 write!(w, "{}", self.f.function_context.prefix())?;
198 }
199 write!(w, " ")?;
200 w.write_all(ctx.as_ref())?;
201 if self.f.with_color {
202 write!(w, "{}", self.f.function_context.suffix())?;
203 }
204 }
205 writeln!(w)?;
206
207 for line in &self.hunk.lines {
208 self.f.write_line_into(line, &mut w)?;
209 }
210
211 Ok(())
212 }
213}
214
215impl Display for HunkDisplay<'_, str> {
216 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
217 if self.f.with_color {
218 write!(f, "{}", self.f.hunk_header.prefix())?;
219 }
220 write!(f, "@@ -{} +{} @@", self.hunk.old_range, self.hunk.new_range)?;
221 if self.f.with_color {
222 write!(f, "{}", self.f.hunk_header.suffix())?;
223 }
224
225 if let Some(ctx) = self.hunk.function_context {
226 write!(f, " ")?;
227 if self.f.with_color {
228 write!(f, "{}", self.f.function_context.prefix())?;
229 }
230 write!(f, " {}", ctx)?;
231 if self.f.with_color {
232 write!(f, "{}", self.f.function_context.suffix())?;
233 }
234 }
235 writeln!(f)?;
236
237 for line in &self.hunk.lines {
238 write!(f, "{}", self.f.fmt_line(line))?;
239 }
240
241 Ok(())
242 }
243}
244
245struct LineDisplay<'a, T: ?Sized> {
246 f: &'a PatchFormatter,
247 line: &'a Line<'a, T>,
248}
249
250impl<T: AsRef<[u8]> + ?Sized> LineDisplay<'_, T> {
251 fn write_into<W: io::Write>(&self, mut w: W) -> io::Result<()> {
252 let (sign, line, style) = match self.line {
253 Line::Context(line) => (' ', line.as_ref(), self.f.context),
254 Line::Delete(line) => ('-', line.as_ref(), self.f.delete),
255 Line::Insert(line) => ('+', line.as_ref(), self.f.insert),
256 };
257
258 if self.f.with_color {
259 write!(w, "{}", style.prefix())?;
260 }
261
262 if self.f.suppress_blank_empty && sign == ' ' && line == b"\n" {
263 w.write_all(line)?;
264 } else {
265 write!(w, "{}", sign)?;
266 w.write_all(line)?;
267 }
268
269 if self.f.with_color {
270 write!(w, "{}", style.suffix())?;
271 }
272
273 if !line.ends_with(b"\n") {
274 writeln!(w)?;
275 if self.f.with_missing_newline_message {
276 writeln!(w, "{}", NO_NEWLINE_AT_EOF)?;
277 }
278 }
279
280 Ok(())
281 }
282}
283
284impl Display for LineDisplay<'_, str> {
285 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
286 let (sign, line, style) = match self.line {
287 Line::Context(line) => (' ', line, self.f.context),
288 Line::Delete(line) => ('-', line, self.f.delete),
289 Line::Insert(line) => ('+', line, self.f.insert),
290 };
291
292 if self.f.with_color {
293 write!(f, "{}", style.prefix())?;
294 }
295
296 if self.f.suppress_blank_empty && sign == ' ' && *line == "\n" {
297 write!(f, "{}", line)?;
298 } else {
299 write!(f, "{}{}", sign, line)?;
300 }
301
302 if self.f.with_color {
303 write!(f, "{}", style.suffix())?;
304 }
305
306 if !line.ends_with('\n') {
307 writeln!(f)?;
308 if self.f.with_missing_newline_message {
309 writeln!(f, "{}", NO_NEWLINE_AT_EOF)?;
310 }
311 }
312
313 Ok(())
314 }
315}