Add screensaver idle command support, autocd directory completion, and unused import cleanup

This commit is contained in:
2026-03-10 12:20:40 -04:00
parent 85e5fc2875
commit bb3db444db
3 changed files with 65 additions and 6 deletions

View File

@@ -31,6 +31,7 @@ use nix::unistd::read;
use crate::builtin::keymap::KeyMapMatch;
use crate::builtin::trap::TrapTarget;
use crate::libsh::error::{self, ShErr, ShErrKind, ShResult};
use crate::libsh::guards::scope_guard;
use crate::libsh::sys::TTY_FILENO;
use crate::libsh::utils::AutoCmdVecUtils;
use crate::parse::execute::{exec_dash_c, exec_input};
@@ -39,7 +40,7 @@ use crate::procio::borrow_fd;
use crate::readline::term::{LineWriter, RawModeGuard, raw_mode};
use crate::readline::{Prompt, ReadlineEvent, ShedVi};
use crate::signal::{GOT_SIGWINCH, JOB_DONE, QUIT_CODE, check_signals, sig_setup, signals_pending};
use crate::state::{AutoCmdKind, read_logic, read_shopts, source_rc, write_jobs, write_meta};
use crate::state::{AutoCmdKind, read_logic, read_shopts, source_rc, write_jobs, write_meta, write_shopts};
use clap::Parser;
use state::{read_vars, write_vars};
@@ -264,14 +265,38 @@ fn shed_interactive(args: ShedArgs) -> ShResult<()> {
PollFlags::POLLIN,
)];
let mut exec_if_timeout = None;
let timeout = if readline.pending_keymap.is_empty() {
let screensaver_cmd = read_shopts(|o| o.prompt.screensaver_cmd.clone());
let screensaver_idle_time = read_shopts(|o| o.prompt.screensaver_idle_time);
if screensaver_idle_time > 0 && !screensaver_cmd.is_empty() {
exec_if_timeout = Some(screensaver_cmd);
PollTimeout::from((screensaver_idle_time * 1000) as u16)
} else {
PollTimeout::MAX
}
} else {
PollTimeout::from(1000u16)
};
match poll(&mut fds, timeout) {
Ok(_) => {}
Ok(0) => {
// We timed out.
if let Some(cmd) = exec_if_timeout {
let prepared = ReadlineEvent::Line(cmd);
let saved_hist_opt = read_shopts(|o| o.core.auto_hist);
let _guard = scopeguard::guard(saved_hist_opt, |opt| {
write_shopts(|o| o.core.auto_hist = opt);
});
write_shopts(|o| o.core.auto_hist = false); // don't save screensaver command to history
match handle_readline_event(&mut readline, Ok(prepared))? {
true => return Ok(()),
false => continue
}
}
}
Err(Errno::EINTR) => {
// Interrupted by signal, loop back to handle it
continue;
@@ -280,6 +305,7 @@ fn shed_interactive(args: ShedArgs) -> ShResult<()> {
eprintln!("poll error: {e}");
break;
}
Ok(_) => {}
}
// Timeout — resolve pending keymap ambiguity

View File

@@ -1,7 +1,7 @@
use std::{
collections::HashSet,
fmt::{Debug, Write},
path::PathBuf,
path::{Path, PathBuf},
sync::Arc,
};
@@ -22,7 +22,7 @@ use crate::{
term::{LineWriter, TermWriter, calc_str_width, get_win_size},
vimode::{ViInsert, ViMode},
},
state::{VarFlags, VarKind, read_jobs, read_logic, read_meta, read_vars, write_vars},
state::{VarFlags, VarKind, read_jobs, read_logic, read_meta, read_shopts, read_vars, write_vars},
};
pub fn complete_signals(start: &str) -> Vec<String> {
@@ -173,6 +173,11 @@ fn complete_commands(start: &str) -> Vec<String> {
.collect()
});
if read_shopts(|o| o.core.autocd) {
let dirs = complete_dirs(start);
candidates.extend(dirs);
}
candidates.sort();
candidates
}

View File

@@ -360,6 +360,8 @@ pub struct ShOptPrompt {
pub linebreak_on_incomplete: bool,
pub leader: String,
pub line_numbers: bool,
pub screensaver_cmd: String,
pub screensaver_idle_time: usize,
}
impl ShOptPrompt {
@@ -431,6 +433,18 @@ impl ShOptPrompt {
};
self.line_numbers = val;
}
"screensaver_cmd" => {
self.screensaver_cmd = val.to_string();
}
"screensaver_idle_time" => {
let Ok(val) = val.parse::<usize>() else {
return Err(ShErr::simple(
ShErrKind::SyntaxErr,
"shopt: expected a positive integer for screensaver_idle_time value",
));
};
self.screensaver_idle_time = val;
}
"custom" => {
todo!()
}
@@ -496,6 +510,16 @@ impl ShOptPrompt {
output.push_str(&format!("{}", self.line_numbers));
Ok(Some(output))
}
"screensaver_cmd" => {
let mut output = String::from("Command to execute as a screensaver after idle timeout\n");
output.push_str(&self.screensaver_cmd);
Ok(Some(output))
}
"screensaver_idle_time" => {
let mut output = String::from("Idle time in seconds before running screensaver_cmd (0 = disabled)\n");
output.push_str(&format!("{}", self.screensaver_idle_time));
Ok(Some(output))
}
_ => Err(ShErr::simple(
ShErrKind::SyntaxErr,
format!("shopt: Unexpected 'prompt' option '{query}'"),
@@ -519,6 +543,8 @@ impl Display for ShOptPrompt {
));
output.push(format!("leader = {}", self.leader));
output.push(format!("line_numbers = {}", self.line_numbers));
output.push(format!("screensaver_cmd = {}", self.screensaver_cmd));
output.push(format!("screensaver_idle_time = {}", self.screensaver_idle_time));
let final_output = output.join("\n");
@@ -537,6 +563,8 @@ impl Default for ShOptPrompt {
linebreak_on_incomplete: true,
leader: "\\".to_string(),
line_numbers: true,
screensaver_cmd: String::new(),
screensaver_idle_time: 0,
}
}
}