feat: Support creating new entries
Signed-off-by: Awiteb <a@4rs.nl>
This commit is contained in:
parent
6acffbf9e1
commit
626b4f8cc5
12 changed files with 113 additions and 14 deletions
|
@ -22,8 +22,8 @@ cargo install --path . # this will install the binary in $HOME/.cargo/bin
|
|||
`../new_name.rs` will move the file to the parent directory and rename it to
|
||||
`new_name.rs`.
|
||||
- You can delete a file/directory with `ctrl+d` keybinding
|
||||
<!-- - You can create a new file with `n+f` keybinding -->
|
||||
<!-- - You can create a new directory with `n+d` keybinding -->
|
||||
- You can create a new file with `n+f` keybinding
|
||||
- You can create a new directory with `n+d` keybinding
|
||||
|
||||
## Implemented features
|
||||
|
||||
|
@ -58,6 +58,7 @@ The key bindings are configurable. For the set of configurable keys and key comb
|
|||
| `left arrow` | `--keybinding.collapse_dir=left` | collapse an entry directory or jump to parent if not collapsable |
|
||||
| `right arrow` | `--keybinding.expand_dir=left` | expand an entry directory |
|
||||
| `r` | `--keybinding.rename_mode=r` | Enter rename mode (To exit rename mode press `quit` keybinding) |
|
||||
| `n` | `--keybinding.new_entry=n` | Enter create mode (To exit rename mode press `quit` keybinding) |
|
||||
| `ctrl+r` | `--keybinding.reload=r` | collapse all directories and reload root directory |
|
||||
| `ctrl+d` | `--keybinding.remove=ctrl+d` | remove a file/directory |
|
||||
| `return` | `--keybinding.file_action=return` | perform configured file action |
|
||||
|
|
|
@ -15,6 +15,7 @@ mod entry_up;
|
|||
mod expand_dir;
|
||||
mod file_action;
|
||||
mod modes;
|
||||
mod new_entry;
|
||||
mod quit;
|
||||
mod reload;
|
||||
mod remove;
|
||||
|
@ -33,7 +34,17 @@ impl<W: Write> EventQueue<W> {
|
|||
(Mode::Normal, k) if k == Key::from(&ck.quit) => self.do_quit(),
|
||||
(Mode::Normal, k) if k == Key::from(&ck.reload) => self.do_reload(),
|
||||
(Mode::Normal, k) if k == Key::from(&ck.rename_mode) => self.do_enter_rename_mode(),
|
||||
(Mode::Normal, k) if k == Key::from(&ck.new_entry) => self.do_enter_create_mode(),
|
||||
(Mode::Normal, k) if k == Key::from(&ck.remove) => self.do_remove_entry(),
|
||||
(Mode::CreateEntry, k) if k == Key::from(&ck.quit) => self.do_enter_normal_mode(),
|
||||
(Mode::CreateEntry, k) if k == Key::from("f") => {
|
||||
self.do_create_file();
|
||||
self.do_enter_normal_mode()
|
||||
}
|
||||
(Mode::CreateEntry, k) if k == Key::from("d") => {
|
||||
self.do_create_dir();
|
||||
self.do_enter_normal_mode()
|
||||
}
|
||||
(Mode::Rename(..), k) if k == Key::from(&ck.quit) => self.do_enter_normal_mode(),
|
||||
(Mode::Rename(new_name), k) if k == Key::from("return") => {
|
||||
if !new_name.chars().all(char::is_whitespace) {
|
||||
|
@ -47,7 +58,7 @@ impl<W: Write> EventQueue<W> {
|
|||
if let Cow::Owned(updated_name) = self.do_handle_rename_input(&new_name, k.inner())
|
||||
{
|
||||
self.pager.mode = Mode::Rename(updated_name);
|
||||
self.update_pager(0);
|
||||
self.do_reload();
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
|
|
|
@ -8,19 +8,25 @@ use crate::{controller::EventQueue, view::Mode};
|
|||
impl<W: Write> EventQueue<W> {
|
||||
pub fn do_enter_normal_mode(&mut self) -> Option<()> {
|
||||
self.pager.mode = Mode::Normal;
|
||||
self.update_pager(0);
|
||||
self.do_reload();
|
||||
Some(())
|
||||
}
|
||||
pub fn do_enter_rename_mode(&mut self) -> Option<()> {
|
||||
self.pager.mode = Mode::Rename(
|
||||
self.pager
|
||||
.current_entry
|
||||
.path
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
);
|
||||
self.update_pager(0);
|
||||
self.do_reload();
|
||||
Some(())
|
||||
}
|
||||
pub fn do_enter_create_mode(&mut self) -> Option<()> {
|
||||
self.pager.mode = Mode::CreateEntry;
|
||||
self.do_reload();
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
|
44
src/controller/key_event_matcher/new_entry.rs
Normal file
44
src/controller/key_event_matcher/new_entry.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2024 Awiteb <a@4rs.nl>
|
||||
|
||||
use std::{fs, io::Write};
|
||||
|
||||
use crate::controller::EventQueue;
|
||||
|
||||
/// Default name for new files, if the name already exists, a number will be appended
|
||||
const NEW_FILE_NAME: &str = "new_file";
|
||||
/// Default name for new directories, if the name already exists, a number will be appended
|
||||
const NEW_DIR_NAME: &str = "new_dir";
|
||||
|
||||
impl<W: Write> EventQueue<W> {
|
||||
/// Create a new file in the current directory
|
||||
pub fn do_create_file(&mut self) -> Option<()> {
|
||||
let mut counter = 1;
|
||||
let mut new_file_path = if self.pager.current_entry.is_expanded {
|
||||
self.pager.current_entry.path.join(NEW_FILE_NAME)
|
||||
} else {
|
||||
self.pager.current_entry.path.with_file_name(NEW_FILE_NAME)
|
||||
};
|
||||
while new_file_path.exists() {
|
||||
new_file_path = new_file_path.with_file_name(format!("{NEW_FILE_NAME}{counter}"));
|
||||
counter += 1
|
||||
}
|
||||
let _ = fs::File::create(new_file_path);
|
||||
Some(())
|
||||
}
|
||||
/// Create a new directory in the current directory
|
||||
pub fn do_create_dir(&mut self) -> Option<()> {
|
||||
let mut counter = 1;
|
||||
let mut new_dir_path = if self.pager.current_entry.is_expanded {
|
||||
self.pager.current_entry.path.join(NEW_DIR_NAME)
|
||||
} else {
|
||||
self.pager.current_entry.path.with_file_name(NEW_DIR_NAME)
|
||||
};
|
||||
while new_dir_path.exists() {
|
||||
new_dir_path = new_dir_path.with_file_name(format!("{NEW_DIR_NAME}{counter}"));
|
||||
counter += 1
|
||||
}
|
||||
let _ = fs::create_dir(new_dir_path);
|
||||
Some(())
|
||||
}
|
||||
}
|
|
@ -8,10 +8,10 @@ use crate::controller::EventQueue;
|
|||
impl<W: Write> EventQueue<W> {
|
||||
/// Remove the current entry
|
||||
pub fn do_remove_entry(&mut self) -> Option<()> {
|
||||
if self.pager.current_entry.is_dir() {
|
||||
let _ = fs::remove_dir_all(&self.pager.current_entry);
|
||||
if self.pager.current_entry.path.is_dir() {
|
||||
let _ = fs::remove_dir_all(&self.pager.current_entry.path);
|
||||
} else {
|
||||
let _ = fs::remove_file(&self.pager.current_entry);
|
||||
let _ = fs::remove_file(&self.pager.current_entry.path);
|
||||
}
|
||||
self.do_reload();
|
||||
Some(())
|
||||
|
|
|
@ -28,9 +28,9 @@ impl<W: Write> EventQueue<W> {
|
|||
|
||||
/// Rename the current file to the new name.
|
||||
pub fn do_rename_current_file(&mut self, new_name: &str) -> Option<()> {
|
||||
let new_path = self.pager.current_entry.with_file_name(new_name);
|
||||
let new_path = self.pager.current_entry.path.with_file_name(new_name);
|
||||
if !new_path.exists() {
|
||||
let _ = fs::rename(&self.pager.current_entry, new_path);
|
||||
let _ = fs::rename(&self.pager.current_entry.path, new_path);
|
||||
};
|
||||
Some(())
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ pub struct Keybinding {
|
|||
pub rename_mode: String,
|
||||
#[serde(default = "Keybinding::default_remove")]
|
||||
pub remove: String,
|
||||
#[serde(default = "Keybinding::default_new_entry")]
|
||||
pub new_entry: String,
|
||||
}
|
||||
|
||||
impl Default for Keybinding {
|
||||
|
@ -38,6 +40,7 @@ impl Default for Keybinding {
|
|||
reload: Self::default_reload(),
|
||||
rename_mode: Self::default_rename_mode(),
|
||||
remove: Self::default_remove(),
|
||||
new_entry: Self::default_new_entry(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,4 +73,7 @@ impl Keybinding {
|
|||
fn default_remove() -> String {
|
||||
String::from("ctrl+d")
|
||||
}
|
||||
fn default_new_entry() -> String {
|
||||
String::from("n")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,9 @@ impl Config {
|
|||
"--keybinding.entry_up" => config.keybinding.entry_up = Self::parse_value((key, value)),
|
||||
"--keybinding.expand_dir" => config.keybinding.expand_dir = Self::parse_value((key, value)),
|
||||
"--keybinding.file_action" => config.keybinding.file_action = Self::parse_value((key, value)),
|
||||
"--keybinding.rename_mode" => config.keybinding.rename_mode = Self::parse_value((key, value)),
|
||||
"--keybinding.new_entry" => config.keybinding.new_entry = Self::parse_value((key, value)),
|
||||
"--keybinding.remove" => config.keybinding.remove = Self::parse_value((key, value)),
|
||||
"--keybinding.quit" => config.keybinding.quit = Self::parse_value((key, value)),
|
||||
"--keybinding.reload" => config.keybinding.reload = Self::parse_value((key, value)),
|
||||
"--setup.working_dir" => config.setup.working_dir = Self::parse_value((key, value)),
|
||||
|
|
|
@ -15,17 +15,36 @@ mod print;
|
|||
mod scroll;
|
||||
mod update;
|
||||
|
||||
pub struct CurrentEntry {
|
||||
pub path: PathBuf,
|
||||
pub is_expanded: bool,
|
||||
}
|
||||
|
||||
pub struct Pager<W: Write> {
|
||||
config: Config,
|
||||
pub mode: Mode,
|
||||
pub cursor_row: i32,
|
||||
pub current_entry: PathBuf,
|
||||
pub current_entry: CurrentEntry,
|
||||
out: W,
|
||||
terminal_cols: i32,
|
||||
terminal_rows: i32,
|
||||
text_row: i32,
|
||||
}
|
||||
|
||||
impl CurrentEntry {
|
||||
fn new(path: impl AsRef<Path>) -> Self {
|
||||
Self {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
is_expanded: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn expand(mut self, yes: bool) -> Self {
|
||||
self.is_expanded = yes;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Pager<W> {
|
||||
pub fn new(config: Config, mut out: W) -> Self {
|
||||
info!("initializing pager");
|
||||
|
@ -40,7 +59,7 @@ impl<W: Write> Pager<W> {
|
|||
.unwrap();
|
||||
|
||||
Self {
|
||||
current_entry: Path::new(&config.setup.working_dir).to_path_buf(),
|
||||
current_entry: CurrentEntry::new(&config.setup.working_dir),
|
||||
config,
|
||||
cursor_row: 0,
|
||||
out,
|
||||
|
|
|
@ -8,6 +8,8 @@ pub enum Mode {
|
|||
Normal,
|
||||
/// Rename mode. Renaming a file.
|
||||
Rename(String),
|
||||
/// Create entry mode. Creating a new file and directory.
|
||||
CreateEntry,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
|
|
|
@ -6,6 +6,8 @@ use crate::{controller::Entrie, view::Pager};
|
|||
use std::{io::Write, path};
|
||||
use termion::terminal_size;
|
||||
|
||||
use super::CurrentEntry;
|
||||
|
||||
impl<W: Write> Pager<W> {
|
||||
fn update_terminal_size(&mut self) {
|
||||
let (terminal_cols_raw, terminal_rows_raw) = terminal_size().unwrap();
|
||||
|
@ -59,8 +61,12 @@ impl<W: Write> Pager<W> {
|
|||
let entry = &entries[index as usize];
|
||||
|
||||
if index == self.cursor_row {
|
||||
self.current_entry =
|
||||
path::absolute(&entry.path).unwrap_or_else(|_| entry.path.clone());
|
||||
self.current_entry = CurrentEntry::new(
|
||||
path::absolute(&entry.path).unwrap_or_else(|_| entry.path.clone()),
|
||||
)
|
||||
.expand(
|
||||
entry.path.is_dir() && entry.display_text.trim().starts_with(['▼', 'v']),
|
||||
);
|
||||
|
||||
let rename_name = self.mode.new_name().map(|new_name| {
|
||||
let filename = entry.path.file_name().unwrap().to_str().unwrap();
|
||||
|
|
|
@ -52,6 +52,7 @@ reload = "ctrl+r"
|
|||
skip_up = "ctrl+up"
|
||||
skip_down = "ctrl+down"
|
||||
remove = "d"
|
||||
new_entry = "n"
|
||||
|
||||
[setup]
|
||||
# the working directory used when starting
|
||||
|
|
Loading…
Reference in a new issue