use std::{
collections::LinkedList,
env::current_dir,
path::{Path, PathBuf},
};
use shrs::prelude::*;
pub struct CdStackState {
down_stack: LinkedList<PathBuf>,
up_stack: LinkedList<PathBuf>,
}
impl CdStackState {
pub fn new() -> Self {
Self {
down_stack: LinkedList::new(),
up_stack: LinkedList::new(),
}
}
pub fn push(&mut self, path: &Path) {
self.down_stack.push_back(path.to_path_buf());
self.up_stack.clear();
}
pub fn down(&mut self) -> Option<PathBuf> {
let top = self.down_stack.pop_back();
if top.is_some() {
self.up_stack.push_back(current_dir().unwrap());
}
top
}
pub fn up(&mut self) -> Option<PathBuf> {
let top = self.up_stack.pop_back();
if top.is_some() {
self.down_stack.push_back(current_dir().unwrap());
}
top
}
}
fn change_dir_hook(
mut state: StateMut<CdStackState>,
hook_ctx: &ChangeDirCtx,
) -> anyhow::Result<()> {
if hook_ctx.old_dir != hook_ctx.new_dir {
state.push(&hook_ctx.old_dir);
}
Ok(())
}
pub fn cd_stack_down(
mut rt: StateMut<Runtime>,
mut state: StateMut<CdStackState>,
sh: &Shell,
) -> anyhow::Result<()> {
if let Some(path) = state.down() {
let _ = set_working_dir(sh, &mut rt, &path, false);
}
Ok(())
}
pub fn cd_stack_up(
mut rt: StateMut<Runtime>,
mut state: StateMut<CdStackState>,
sh: &Shell,
) -> anyhow::Result<()> {
if let Some(path) = state.up() {
let _ = set_working_dir(sh, &mut rt, &path, false);
}
Ok(())
}
pub struct CdStackPlugin;
impl Plugin for CdStackPlugin {
fn init(&self, shell: &mut ShellConfig) -> anyhow::Result<()> {
shell.hooks.insert(change_dir_hook);
Ok(())
}
fn post_init(&self, _sh: &mut Shell, states: &mut States) -> anyhow::Result<()> {
let cd_stack_state = CdStackState::new();
states.insert(cd_stack_state);
Ok(())
}
fn meta(&self) -> PluginMeta {
PluginMeta::new(
"Cd Stack",
"Provides the ability to quickly navigate directories like a stack.",
None,
)
}
}