Add screensaver idle command support, autocd directory completion, and unused import cleanup
This commit is contained in:
30
src/main.rs
30
src/main.rs
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
28
src/shopt.rs
28
src/shopt.rs
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user