fix: place command status left of battery as a separate segment
This commit is contained in:
+6
-3
@@ -130,7 +130,7 @@ impl ChromeLine {
|
||||
Self {
|
||||
left: vec!["tabs".into()],
|
||||
center: Vec::new(),
|
||||
right: vec!["battery".into(), "shell".into()],
|
||||
right: vec!["shell".into(), "battery".into()],
|
||||
segments: BTreeMap::from([
|
||||
("tabs".into(), default_segment("tabs")),
|
||||
("battery".into(), default_segment("battery")),
|
||||
@@ -213,12 +213,15 @@ impl ChromeLine {
|
||||
|
||||
fn default_segment(name: &str) -> SegmentConfig {
|
||||
let mut segment = SegmentConfig::default();
|
||||
if matches!(name, "tabs" | "position" | "battery") {
|
||||
if matches!(name, "tabs" | "position" | "battery" | "shell") {
|
||||
segment.leading_edge = "░▓".into();
|
||||
}
|
||||
if matches!(name, "tabs" | "mode") {
|
||||
if matches!(name, "tabs" | "mode" | "shell") {
|
||||
segment.trailing_edge = "▓░".into();
|
||||
}
|
||||
if name == "shell" {
|
||||
segment.margin[1] = 1;
|
||||
}
|
||||
if name == "tabs" {
|
||||
segment.title = "{index} {view}{name}{modified}".into();
|
||||
segment.first_leading_edge = Some(String::new());
|
||||
|
||||
+12
-3
@@ -2233,6 +2233,7 @@ pub struct Editor {
|
||||
battery_label: String,
|
||||
shell_label: String,
|
||||
command_output: Option<ShellOutput>,
|
||||
queued_command_outputs: VecDeque<ShellOutput>,
|
||||
git_blame: bool,
|
||||
gutter_style: crate::gutter::GutterStyle,
|
||||
line_blame: Option<(BlameSource, Arc<crate::blame::LineBlame>, String)>,
|
||||
@@ -2351,6 +2352,7 @@ impl Editor {
|
||||
battery_label: String::new(),
|
||||
shell_label: String::new(),
|
||||
command_output: None,
|
||||
queued_command_outputs: VecDeque::new(),
|
||||
git_blame: true,
|
||||
gutter_style: crate::gutter::GutterStyle::default(),
|
||||
line_blame: None,
|
||||
@@ -6996,7 +6998,7 @@ impl Editor {
|
||||
.chars()
|
||||
.map(|ch| if ch.is_control() { '�' } else { ch })
|
||||
.collect();
|
||||
self.command_output = Some(ShellOutput {
|
||||
let output = ShellOutput {
|
||||
title: format!("{status} · !{command}"),
|
||||
wrapping: false,
|
||||
wrapped_rows: Cell::new(1),
|
||||
@@ -7007,7 +7009,12 @@ impl Editor {
|
||||
left: 0,
|
||||
max_column,
|
||||
},
|
||||
});
|
||||
};
|
||||
if self.command_output.is_some() {
|
||||
self.queued_command_outputs.push_back(output);
|
||||
} else {
|
||||
self.command_output = Some(output);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_command_output(&mut self, input: &Input) -> bool {
|
||||
@@ -7041,7 +7048,9 @@ impl Editor {
|
||||
};
|
||||
let content = &mut output.content;
|
||||
match input {
|
||||
Input::Escape | Input::Enter => self.command_output = None,
|
||||
Input::Escape | Input::Enter => {
|
||||
self.command_output = self.queued_command_outputs.pop_front()
|
||||
}
|
||||
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(rows.saturating_sub(1));
|
||||
|
||||
+55
-34
@@ -439,6 +439,13 @@ fn text_fits(lines: &[String], limit: usize) -> bool {
|
||||
.is_some()
|
||||
}
|
||||
|
||||
struct ShellCommand {
|
||||
command: String,
|
||||
worker: crate::shell::Worker,
|
||||
started: Instant,
|
||||
label: String,
|
||||
}
|
||||
|
||||
/// Owns command eligibility and execution hosts together. Frontends can inspect
|
||||
/// the editor, but cannot independently mutate its plugin registry.
|
||||
pub struct EditorSession {
|
||||
@@ -468,7 +475,7 @@ pub struct EditorSession {
|
||||
picker_source: Option<PathBuf>,
|
||||
battery: Option<crate::battery::Battery>,
|
||||
battery_enabled: bool,
|
||||
shell: Option<(String, crate::shell::Worker, Instant)>,
|
||||
shell: Vec<ShellCommand>,
|
||||
last_file_refresh: Instant,
|
||||
last_diff_refresh: Instant,
|
||||
pub(crate) diff_follow_working_copy: bool,
|
||||
@@ -596,7 +603,7 @@ impl EditorSession {
|
||||
picker_source: None,
|
||||
battery: None,
|
||||
battery_enabled: false,
|
||||
shell: None,
|
||||
shell: Vec::new(),
|
||||
last_file_refresh: Instant::now(),
|
||||
last_diff_refresh: Instant::now(),
|
||||
diff_follow_working_copy: config.diff_follow_working_copy,
|
||||
@@ -830,18 +837,18 @@ impl EditorSession {
|
||||
fn dispatch(&mut self, invocation: Invocation) {
|
||||
if invocation.plugin == "core" {
|
||||
if invocation.command == "shell" {
|
||||
if self.shell.is_some() {
|
||||
self.editor
|
||||
.set_message("A shell command is already running");
|
||||
return;
|
||||
}
|
||||
let command = invocation.args;
|
||||
match crate::shell::Worker::start(
|
||||
command.clone(),
|
||||
self.editor.project_root().to_path_buf(),
|
||||
) {
|
||||
Ok(worker) => {
|
||||
self.shell = Some((command, worker, Instant::now()));
|
||||
self.shell.push(ShellCommand {
|
||||
command,
|
||||
worker,
|
||||
started: Instant::now(),
|
||||
label: String::new(),
|
||||
});
|
||||
self.poll_shell_at(Instant::now());
|
||||
}
|
||||
Err(error) => self.editor.show_command_output(command, Err(error)),
|
||||
@@ -1772,34 +1779,48 @@ impl EditorSession {
|
||||
}
|
||||
|
||||
fn poll_shell_at(&mut self, now: Instant) -> bool {
|
||||
let Some((command, worker, started)) = &mut self.shell else {
|
||||
return false;
|
||||
};
|
||||
if let Some(result) = worker.poll() {
|
||||
let command = command.clone();
|
||||
self.shell = None;
|
||||
self.editor.set_shell_label(String::new());
|
||||
self.editor.show_command_output(command, result);
|
||||
return true;
|
||||
}
|
||||
let elapsed = now.saturating_duration_since(*started);
|
||||
if elapsed < Duration::from_millis(200) {
|
||||
return false;
|
||||
}
|
||||
const FRAMES: [char; 4] = ['|', '/', '-', '\\'];
|
||||
let frame = FRAMES[((elapsed.as_millis() / 100) % 4) as usize];
|
||||
if self.editor.shell_label().ends_with(frame) {
|
||||
return false;
|
||||
}
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
let mut graphemes = command.trim().graphemes(true);
|
||||
let mut label: String = graphemes.by_ref().take(3).collect();
|
||||
if graphemes.next().is_some() {
|
||||
label.push_str("...");
|
||||
const FRAMES: [char; 4] = ['|', '/', '-', '\\'];
|
||||
let mut changed = false;
|
||||
self.shell.retain_mut(|running| {
|
||||
if let Some(result) = running.worker.poll() {
|
||||
self.editor
|
||||
.show_command_output(std::mem::take(&mut running.command), result);
|
||||
changed = true;
|
||||
return false;
|
||||
}
|
||||
let elapsed = now.saturating_duration_since(running.started);
|
||||
if elapsed >= Duration::from_millis(200) {
|
||||
let frame = FRAMES[((elapsed.as_millis() / 100) % 4) as usize];
|
||||
if !running.label.ends_with(frame) {
|
||||
running.label.clear();
|
||||
let mut graphemes = running.command.trim().graphemes(true);
|
||||
running.label.extend(graphemes.by_ref().take(3));
|
||||
if graphemes.next().is_some() {
|
||||
running.label.push_str("...");
|
||||
}
|
||||
running.label.push(' ');
|
||||
running.label.push(frame);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
if changed {
|
||||
let mut label = String::new();
|
||||
for running in self
|
||||
.shell
|
||||
.iter()
|
||||
.filter(|running| !running.label.is_empty())
|
||||
{
|
||||
if !label.is_empty() {
|
||||
label.push_str(" ");
|
||||
}
|
||||
label.push_str(&running.label);
|
||||
}
|
||||
self.editor.set_shell_label(label);
|
||||
}
|
||||
label.push(' ');
|
||||
label.push(frame);
|
||||
self.editor.set_shell_label(label)
|
||||
changed
|
||||
}
|
||||
|
||||
fn refresh_files_at(&mut self, now: Instant) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user