feat: toggle wrapping in shell output windows
This commit is contained in:
+30
-4
@@ -140,6 +140,9 @@ pub struct StartupDiagnostic {
|
||||
pub(crate) struct ShellOutput {
|
||||
pub(crate) title: String,
|
||||
pub(crate) content: StartupDiagnostic,
|
||||
pub(crate) wrapping: bool,
|
||||
pub(crate) wrapped_rows: Cell<usize>,
|
||||
pub(crate) wrapped_lines: std::cell::RefCell<(usize, Vec<(usize, crate::wrap::Segment)>)>,
|
||||
}
|
||||
|
||||
pub struct Hover {
|
||||
@@ -6992,6 +6995,9 @@ impl Editor {
|
||||
.collect();
|
||||
self.command_output = Some(ShellOutput {
|
||||
title: format!("{status} · !{command}"),
|
||||
wrapping: false,
|
||||
wrapped_rows: Cell::new(1),
|
||||
wrapped_lines: Default::default(),
|
||||
content: StartupDiagnostic {
|
||||
lines,
|
||||
top: 0,
|
||||
@@ -7002,25 +7008,45 @@ impl Editor {
|
||||
}
|
||||
|
||||
fn handle_command_output(&mut self, input: &Input) -> bool {
|
||||
let toggle = input == &Input::Char('w')
|
||||
|| matches!(
|
||||
self.bindings
|
||||
.get(&self.binding_mode())
|
||||
.and_then(|keys| keys.get(input)),
|
||||
Some(Binding::Action(Action::WordWrapToggle))
|
||||
);
|
||||
let Some(output) = &mut self.command_output else {
|
||||
return false;
|
||||
};
|
||||
if toggle {
|
||||
output.wrapping = !output.wrapping;
|
||||
output.content.top = 0;
|
||||
output.content.left = 0;
|
||||
return true;
|
||||
}
|
||||
let rows = if output.wrapping {
|
||||
output.wrapped_rows.get()
|
||||
} else {
|
||||
output.content.lines.len()
|
||||
};
|
||||
let content = &mut output.content;
|
||||
match input {
|
||||
Input::Escape | Input::Enter => self.command_output = None,
|
||||
Input::Up | Input::Ctrl('k') => content.top = content.top.saturating_sub(1),
|
||||
Input::Down | Input::Ctrl('j') => {
|
||||
content.top = content.top.saturating_add(1).min(content.lines.len() - 1);
|
||||
content.top = content.top.saturating_add(1).min(rows.saturating_sub(1));
|
||||
}
|
||||
Input::Left | Input::Ctrl('h') => content.left = content.left.saturating_sub(1),
|
||||
Input::Right | Input::Ctrl('l') => {
|
||||
Input::Left | Input::Ctrl('h') if !output.wrapping => {
|
||||
content.left = content.left.saturating_sub(1)
|
||||
}
|
||||
Input::Right | Input::Ctrl('l') if !output.wrapping => {
|
||||
content.left = content.left.saturating_add(1).min(content.max_column);
|
||||
}
|
||||
Input::Home => {
|
||||
content.top = 0;
|
||||
content.left = 0;
|
||||
}
|
||||
Input::End => content.top = content.lines.len() - 1,
|
||||
Input::End => content.top = rows.saturating_sub(1),
|
||||
_ => {}
|
||||
}
|
||||
true
|
||||
|
||||
+53
-18
@@ -2906,7 +2906,7 @@ fn draw_overlays(
|
||||
draw_startup_diagnostic(frame, diagnostic, area);
|
||||
}
|
||||
if let Some(output) = editor.command_output() {
|
||||
draw_diagnostic_panel(frame, &output.content, area, &output.title, false);
|
||||
draw_diagnostic_panel(frame, &output.content, area, &output.title, Some(output));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4631,7 +4631,7 @@ fn floating_panel(area: Rect) -> Rect {
|
||||
}
|
||||
|
||||
fn draw_startup_diagnostic(frame: &mut Frame, diagnostic: &StartupDiagnostic, area: Rect) {
|
||||
draw_diagnostic_panel(frame, diagnostic, area, "Configuration error", true);
|
||||
draw_diagnostic_panel(frame, diagnostic, area, "Configuration error", None);
|
||||
}
|
||||
|
||||
fn draw_diagnostic_panel(
|
||||
@@ -4639,8 +4639,9 @@ fn draw_diagnostic_panel(
|
||||
diagnostic: &StartupDiagnostic,
|
||||
area: Rect,
|
||||
title: &str,
|
||||
startup: bool,
|
||||
output: Option<&crate::editor::ShellOutput>,
|
||||
) {
|
||||
let startup = output.is_none();
|
||||
let panel = floating_panel(area);
|
||||
frame.render_widget(Clear, panel);
|
||||
let bordered = panel.width >= 4 && panel.height >= 4;
|
||||
@@ -4661,26 +4662,60 @@ fn draw_diagnostic_panel(
|
||||
Constraint::Length(u16::from(content.height >= 3)),
|
||||
])
|
||||
.areas(content);
|
||||
let lines: Vec<Line> = diagnostic
|
||||
.lines
|
||||
.iter()
|
||||
.skip(diagnostic.top)
|
||||
.take(usize::from(text_area.height))
|
||||
.map(|line| {
|
||||
let line = display_text(line);
|
||||
Line::from(visible_line(
|
||||
&line,
|
||||
display_column(&line, diagnostic.left),
|
||||
usize::from(text_area.width),
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
let lines: Vec<Line> = if let Some(output) = output.filter(|output| output.wrapping) {
|
||||
let width = usize::from(text_area.width).max(1);
|
||||
let mut cached = output.wrapped_lines.borrow_mut();
|
||||
if cached.0 != width {
|
||||
let mut cache = crate::wrap::WrapCache::default();
|
||||
cache.prepare((0, 0), width);
|
||||
cached.1.clear();
|
||||
for (row, line) in diagnostic.lines.iter().enumerate() {
|
||||
let segments = cache.segments(row, line);
|
||||
for (index, segment) in segments.iter().enumerate() {
|
||||
// Output has no insertion caret: omit its extra empty continuation.
|
||||
if index == 0 || segment.start != segment.end {
|
||||
cached.1.push((row, *segment));
|
||||
}
|
||||
}
|
||||
}
|
||||
cached.0 = width;
|
||||
}
|
||||
output.wrapped_rows.set(cached.1.len());
|
||||
cached
|
||||
.1
|
||||
.iter()
|
||||
.skip(diagnostic.top.min(cached.1.len().saturating_sub(1)))
|
||||
.take(usize::from(text_area.height))
|
||||
.map(|(row, segment)| {
|
||||
Line::from(visible_line(
|
||||
&diagnostic.lines[*row],
|
||||
segment.start,
|
||||
(segment.end - segment.start).min(width),
|
||||
))
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
diagnostic
|
||||
.lines
|
||||
.iter()
|
||||
.skip(diagnostic.top)
|
||||
.take(usize::from(text_area.height))
|
||||
.map(|line| {
|
||||
let line = display_text(line);
|
||||
Line::from(visible_line(
|
||||
&line,
|
||||
display_column(&line, diagnostic.left),
|
||||
usize::from(text_area.width),
|
||||
))
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
frame.render_widget(Paragraph::new(lines), text_area);
|
||||
frame.render_widget(
|
||||
Paragraph::new(if startup {
|
||||
"Arrows scroll · Home reset · Enter/Esc continue"
|
||||
} else {
|
||||
"Arrows scroll · Home reset · Enter/Esc close"
|
||||
"w wrap · Arrows scroll · Home reset · Enter/Esc close"
|
||||
})
|
||||
.style(Style::default().fg(Color::DarkGray)),
|
||||
help_area,
|
||||
|
||||
Reference in New Issue
Block a user