1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use lazy_static::lazy_static;
use crate::cli;
use crate::delta::{State, StateMachine};
use crate::style;
use crate::utils::process::{self, CallingProcess};
use unicode_segmentation::UnicodeSegmentation;
lazy_static! {
static ref IS_WORD_DIFF: bool = match process::calling_process().as_deref() {
Some(
CallingProcess::GitDiff(cmd_line)
| CallingProcess::GitShow(cmd_line, _)
| CallingProcess::GitLog(cmd_line)
| CallingProcess::GitReflog(cmd_line),
) =>
cmd_line.long_options.contains("--word-diff")
|| cmd_line.long_options.contains("--word-diff-regex")
|| cmd_line.long_options.contains("--color-words"),
_ => false,
};
}
impl<'a> StateMachine<'a> {
#[inline]
fn test_hunk_line(&self) -> bool {
matches!(
self.state,
State::HunkHeader(_, _) | State::HunkZero | State::HunkMinus(_) | State::HunkPlus(_)
) && !&*IS_WORD_DIFF
}
/// Handle a hunk line, i.e. a minus line, a plus line, or an unchanged line.
// In the case of a minus or plus line, we store the line in a
// buffer. When we exit the changed region we process the collected
// minus and plus lines jointly, in order to paint detailed
// highlighting according to inferred edit operations. In the case of
// an unchanged line, we paint it immediately.
pub fn handle_hunk_line(&mut self) -> std::io::Result<bool> {
// A true hunk line should start with one of: '+', '-', ' '. However, handle_hunk_line
// handles all lines until the state transitions away from the hunk states.
if !self.test_hunk_line() {
return Ok(false);
}
// Don't let the line buffers become arbitrarily large -- if we
// were to allow that, then for a large deleted/added file we
// would process the entire file before painting anything.
if self.painter.minus_lines.len() > self.config.line_buffer_size
|| self.painter.plus_lines.len() > self.config.line_buffer_size
{
self.painter.paint_buffered_minus_and_plus_lines();
}
if let State::HunkHeader(line, raw_line) = &self.state.clone() {
self.emit_hunk_header_line(line, raw_line)?;
}
self.state = match self.line.chars().next() {
Some('-') => {
if let State::HunkPlus(_) = self.state {
// We have just entered a new subhunk; process the previous one
// and flush the line buffers.
self.painter.paint_buffered_minus_and_plus_lines();
}
let state = match self.config.inspect_raw_lines {
cli::InspectRawLines::True
if style::line_has_style_other_than(
&self.raw_line,
[*style::GIT_DEFAULT_MINUS_STYLE, self.config.git_minus_style].iter(),
) =>
{
State::HunkMinus(Some(self.painter.prepare_raw_line(&self.raw_line)))
}
_ => State::HunkMinus(None),
};
self.painter
.minus_lines
.push((self.painter.prepare(&self.line), state.clone()));
state
}
Some('+') => {
let state = match self.config.inspect_raw_lines {
cli::InspectRawLines::True
if style::line_has_style_other_than(
&self.raw_line,
[*style::GIT_DEFAULT_PLUS_STYLE, self.config.git_plus_style].iter(),
) =>
{
State::HunkPlus(Some(self.painter.prepare_raw_line(&self.raw_line)))
}
_ => State::HunkPlus(None),
};
self.painter
.plus_lines
.push((self.painter.prepare(&self.line), state.clone()));
state
}
Some(' ') => {
self.painter.paint_buffered_minus_and_plus_lines();
self.painter.paint_zero_line(&self.line);
State::HunkZero
}
_ => {
// The first character here could be e.g. '\' from '\ No newline at end of file'. This
// is not a hunk line, but the parser does not have a more accurate state corresponding
// to this.
// We are in a zero (unchanged) line, therefore we have just exited a subhunk (a
// sequence of consecutive minus (removed) and/or plus (added) lines). Process that
// subhunk and flush the line buffers.
self.painter.paint_buffered_minus_and_plus_lines();
self.painter
.output_buffer
.push_str(&self.painter.expand_tabs(self.raw_line.graphemes(true)));
self.painter.output_buffer.push('\n');
State::HunkZero
}
};
self.painter.emit()?;
Ok(true)
}
}