Skip to content

Commit

Permalink
Add multiple renaming
Browse files Browse the repository at this point in the history
  • Loading branch information
kyoheiu committed Jan 10, 2025
1 parent c709936 commit 316179f
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 19 deletions.
23 changes: 14 additions & 9 deletions src/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub struct Operation {
pub enum OpKind {
Delete(DeletedFiles),
Put(PutFiles),
Rename(RenamedFile),
Rename(Vec<(PathBuf, PathBuf)>),
}

#[derive(Debug, Clone)]
Expand All @@ -30,12 +30,6 @@ pub struct PutFiles {
pub dir: PathBuf,
}

#[derive(Debug, Clone)]
pub struct RenamedFile {
pub original_name: PathBuf,
pub new_name: PathBuf,
}

impl Operation {
/// Discard undone operations when new one is pushed.
pub fn branch(&mut self) {
Expand Down Expand Up @@ -63,7 +57,12 @@ fn log(op: &OpKind) {
info!("DELETE: {:?}", item_to_pathvec(&op.original));
}
OpKind::Rename(op) => {
info!("RENAME: {:?} -> {:?}", op.original_name, op.new_name);
info!(
"RENAME: {:?}",
op.iter()
.map(|v| format!("{:?} -> {:?}", v.0, v.1))
.collect::<Vec<String>>()
);
}
}
}
Expand All @@ -85,7 +84,13 @@ pub fn relog(op: &OpKind, undo: bool) {
}
OpKind::Rename(op) => {
result.push_str("RENAME");
info!("{} {:?} -> {:?}", result, op.original_name, op.new_name);
info!(
"{} {:?}",
result,
op.iter()
.map(|v| format!("{:?} -> {:?}", v.0, v.1))
.collect::<Vec<String>>()
);
}
}
}
Expand Down
35 changes: 28 additions & 7 deletions src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1362,8 +1362,31 @@ fn _run(mut state: State, session_path: PathBuf) -> Result<(), FxError> {

//rename
KeyCode::Char('c') => {
//In visual mode, this is disabled.
//In visual mode, you can rename multiple items in default editor.
if state.v_start.is_some() {
let items: Vec<ItemBuffer> = state
.list
.iter()
.filter(|item| item.selected)
.map(ItemBuffer::new)
.collect();
execute!(screen, EnterAlternateScreen)?;
let mut err: Option<FxError> = None;
if let Err(e) = state.rename_multiple_items(&items) {
err = Some(e);
}
execute!(screen, EnterAlternateScreen)?;
hide_cursor();
state.reset_selection();
state.reload(state.layout.y)?;
if let Some(e) = err {
print_warning(e, state.layout.y);
} else {
print_info(
format!("Renamed {} items.", items.len()),
state.layout.y,
);
}
continue;
}
if len == 0 {
Expand Down Expand Up @@ -1511,12 +1534,10 @@ fn _run(mut state: State, session_path: PathBuf) -> Result<(), FxError> {
}

state.operations.branch();
state.operations.push(OpKind::Rename(
RenamedFile {
original_name: item.file_path.clone(),
new_name: to,
},
));
state.operations.push(OpKind::Rename(vec![(
item.file_path.clone(),
to,
)]));

hide_cursor();
state.reload(state.layout.y)?;
Expand Down
56 changes: 53 additions & 3 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ impl Registers {
}
}

/// To avoid cost copying ItemInfo, use ItemBuffer when tinkering with register.
/// To avoid cost copying ItemInfo, use ItemBuffer
/// when tinkering with register or multiple renaming.
#[derive(Debug, Clone)]
pub struct ItemBuffer {
pub file_type: FileType,
Expand Down Expand Up @@ -921,7 +922,9 @@ impl State {
pub fn undo(&mut self, op: &OpKind) -> Result<(), FxError> {
match op {
OpKind::Rename(op) => {
std::fs::rename(&op.new_name, &op.original_name)?;
for (original, new) in op {
std::fs::rename(new, original)?;
}
self.operations.pos += 1;
self.update_list()?;
self.clear_and_show_headline();
Expand Down Expand Up @@ -959,7 +962,9 @@ impl State {
pub fn redo(&mut self, op: &OpKind) -> Result<(), FxError> {
match op {
OpKind::Rename(op) => {
std::fs::rename(&op.original_name, &op.new_name)?;
for (original, new) in op {
std::fs::rename(original, new)?;
}
self.operations.pos -= 1;
self.update_list()?;
self.clear_and_show_headline();
Expand Down Expand Up @@ -1287,6 +1292,51 @@ impl State {
self.list = result;
}

/// Rename selected items at once.
pub fn rename_multiple_items(&mut self, items: &[ItemBuffer]) -> Result<(), FxError> {
let names: Vec<&str> = items.iter().map(|item| item.file_name.as_str()).collect();
let mut file = tempfile::NamedTempFile::new()?;
writeln!(file, "{}", names.join("\n"))?;

let mut default = Command::new(&self.default);
let path = file.into_temp_path();
if let Err(e) = default
.arg(&path)
.status()
.map_err(|_| FxError::DefaultEditor)
{
Err(e)
} else {
let new_names = fs::read_to_string(&path)?;
let new_names: Vec<&str> = new_names
.split('\n')
.filter(|name| !name.is_empty())
.collect();
if new_names.len() != items.len() {
Err(FxError::Io(
format!(
"Rename failed: Expected {} names, but received {} names",
items.len(),
new_names.len()
)
.to_string(),
))
} else {
let mut result: Vec<(PathBuf, PathBuf)> = vec![];
for (i, new_name) in new_names.iter().enumerate() {
let mut to = self.current_dir.clone();
to.push(new_name);
std::fs::rename(&items[i].file_path, &to)?;
result.push((items[i].file_path.clone(), to))
}
self.operations.branch();
self.operations.push(OpKind::Rename(result));

Ok(())
}
}
}

/// Reset all item's selected state and exit the select mode.
pub fn reset_selection(&mut self) {
for item in self.list.iter_mut() {
Expand Down

0 comments on commit 316179f

Please sign in to comment.