Skip to content

Commit 914cbe8

Browse files
UI: Reduce context in config diff when space is limited
1 parent 826e262 commit 914cbe8

File tree

2 files changed

+147
-2
lines changed

2 files changed

+147
-2
lines changed

src/ui/full_term/problems_ui.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,8 +481,13 @@ impl ProblemsUi {
481481
return;
482482
};
483483

484-
let lines = config_diff_lines(&self.config_path, &**edit, &self.edit_opts())
485-
.unwrap_or_else(error_lines);
484+
let lines = config_diff_lines(
485+
&self.config_path,
486+
&**edit,
487+
&self.edit_opts(),
488+
(area.height as usize).saturating_sub(4),
489+
)
490+
.unwrap_or_else(error_lines);
486491

487492
let block = Block::default().title("Edit details").borders(Borders::ALL);
488493
let paragraph = Paragraph::new(lines)
@@ -740,6 +745,7 @@ fn config_diff_lines(
740745
config_path: &Path,
741746
edit: &dyn Edit,
742747
opts: &EditOpts,
748+
max_lines: usize,
743749
) -> Result<Vec<Line<'static>>> {
744750
let mut lines = Vec::new();
745751
lines.push(Line::from(edit.help().to_string()));
@@ -755,6 +761,7 @@ fn config_diff_lines(
755761
lines.push(Line::from(""));
756762
lines.push(Line::from("=== Diff of cackle.toml ==="));
757763
}
764+
diff::remove_excess_context(&mut diff, max_lines - lines.len());
758765
lines.append(&mut diff);
759766
Ok(lines)
760767
}

src/ui/full_term/problems_ui/diff.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,72 @@ pub(super) fn diff_lines(original: &str, updated: &str) -> Vec<Line<'static>> {
5656
lines
5757
}
5858

59+
/// Attempts, where possible to trim the supplied diff to less than `max_lines`.
60+
pub(super) fn remove_excess_context(lines: &mut Vec<Line>, max_lines: usize) {
61+
struct ContextBlock {
62+
start: usize,
63+
length: usize,
64+
to_take: usize,
65+
}
66+
67+
let mut blocks = Vec::new();
68+
let mut current_block = None;
69+
for (offset, line) in lines.iter().enumerate() {
70+
let is_context = line
71+
.spans
72+
.first()
73+
.map(|span| span.content.starts_with(' '))
74+
.unwrap_or(false);
75+
if is_context {
76+
current_block
77+
.get_or_insert(ContextBlock {
78+
start: offset,
79+
length: 0,
80+
to_take: 0,
81+
})
82+
.length += 1;
83+
} else if let Some(block) = current_block.take() {
84+
blocks.push(block);
85+
}
86+
}
87+
blocks.extend(current_block);
88+
89+
let mut to_reclaim = (lines.len() as isize).saturating_sub(max_lines as isize);
90+
while to_reclaim > 0 {
91+
if let Some(block) = blocks.iter_mut().max_by_key(|b| b.length - b.to_take) {
92+
if block.length - block.to_take == 0 {
93+
// We've run out of context to remove.
94+
break;
95+
}
96+
if block.to_take > 0 {
97+
to_reclaim -= 1;
98+
}
99+
block.to_take += 1;
100+
}
101+
}
102+
103+
let old_lines = std::mem::take(lines);
104+
let mut block_index = 0;
105+
blocks.retain(|block| block.to_take > 0);
106+
for (offset, line) in old_lines.into_iter().enumerate() {
107+
let Some(block) = blocks.get(block_index) else {
108+
lines.push(line);
109+
continue;
110+
};
111+
let skip_start = block.start + (block.length - block.to_take) / 2;
112+
let skip_end = skip_start + block.to_take;
113+
if offset < skip_start {
114+
lines.push(line);
115+
continue;
116+
}
117+
if offset >= skip_end {
118+
lines.push(Line::from("..."));
119+
lines.push(line);
120+
block_index += 1;
121+
}
122+
}
123+
}
124+
59125
#[test]
60126
fn test_diff_lines() {
61127
fn line_to_string(line: &Line) -> String {
@@ -115,3 +181,75 @@ fn test_diff_lines() {
115181
];
116182
assert_eq!(lines, expected);
117183
}
184+
185+
#[test]
186+
fn test_remove_excess_context() {
187+
fn to_lines(text: &str) -> Vec<Line<'static>> {
188+
text.lines()
189+
.filter_map(|l| {
190+
// Ignore @ - it's just there to tell indoc where the left margin is.
191+
if !l.starts_with('@') {
192+
Some(Line::from(l.to_owned()))
193+
} else {
194+
None
195+
}
196+
})
197+
.collect()
198+
}
199+
200+
fn to_text(lines: &[Line]) -> String {
201+
lines
202+
.iter()
203+
.map(|line| {
204+
line.spans
205+
.iter()
206+
.map(|s| s.content.as_ref())
207+
.collect::<Vec<_>>()
208+
.join("")
209+
})
210+
.collect::<Vec<_>>()
211+
.join("\n")
212+
}
213+
214+
use indoc::indoc;
215+
216+
let mut lines = to_lines(indoc! {r#"
217+
@
218+
[common]
219+
-version = 1
220+
+version = 2
221+
a
222+
b
223+
c
224+
d
225+
e
226+
f
227+
g
228+
h
229+
i
230+
j
231+
k
232+
+foo = 1
233+
l
234+
m
235+
n
236+
"#});
237+
remove_excess_context(&mut lines, 11);
238+
assert_eq!(
239+
to_text(&lines),
240+
to_text(&to_lines(indoc! {r#"
241+
@
242+
[common]
243+
-version = 1
244+
+version = 2
245+
a
246+
...
247+
j
248+
k
249+
+foo = 1
250+
l
251+
m
252+
n
253+
"#}))
254+
);
255+
}

0 commit comments

Comments
 (0)