mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-28 17:58:42 +00:00
Implement cut/paste for ProjectPanel
This commit is contained in:
parent
6c145b2abc
commit
37a0c7f046
3 changed files with 120 additions and 6 deletions
|
@ -22,7 +22,7 @@ use std::{
|
||||||
collections::{hash_map, HashMap},
|
collections::{hash_map, HashMap},
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
path::PathBuf,
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use unicase::UniCase;
|
use unicase::UniCase;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
@ -37,6 +37,7 @@ pub struct ProjectPanel {
|
||||||
selection: Option<Selection>,
|
selection: Option<Selection>,
|
||||||
edit_state: Option<EditState>,
|
edit_state: Option<EditState>,
|
||||||
filename_editor: ViewHandle<Editor>,
|
filename_editor: ViewHandle<Editor>,
|
||||||
|
clipboard_entry: Option<ClipboardEntry>,
|
||||||
context_menu: ViewHandle<ContextMenu>,
|
context_menu: ViewHandle<ContextMenu>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +56,18 @@ struct EditState {
|
||||||
processing_filename: Option<String>,
|
processing_filename: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum ClipboardEntry {
|
||||||
|
Copied {
|
||||||
|
worktree_id: WorktreeId,
|
||||||
|
entry_id: ProjectEntryId,
|
||||||
|
},
|
||||||
|
Cut {
|
||||||
|
worktree_id: WorktreeId,
|
||||||
|
entry_id: ProjectEntryId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
struct EntryDetails {
|
struct EntryDetails {
|
||||||
filename: String,
|
filename: String,
|
||||||
|
@ -65,6 +78,7 @@ struct EntryDetails {
|
||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
is_editing: bool,
|
is_editing: bool,
|
||||||
is_processing: bool,
|
is_processing: bool,
|
||||||
|
is_cut: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -116,7 +130,11 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
cx.add_action(ProjectPanel::copy);
|
cx.add_action(ProjectPanel::copy);
|
||||||
cx.add_action(ProjectPanel::copy_path);
|
cx.add_action(ProjectPanel::copy_path);
|
||||||
cx.add_action(ProjectPanel::cut);
|
cx.add_action(ProjectPanel::cut);
|
||||||
cx.add_action(ProjectPanel::paste);
|
cx.add_action(
|
||||||
|
|this: &mut ProjectPanel, action: &Paste, cx: &mut ViewContext<ProjectPanel>| {
|
||||||
|
this.paste(action, cx);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
@ -172,6 +190,7 @@ impl ProjectPanel {
|
||||||
selection: None,
|
selection: None,
|
||||||
edit_state: None,
|
edit_state: None,
|
||||||
filename_editor,
|
filename_editor,
|
||||||
|
clipboard_entry: None,
|
||||||
context_menu: cx.add_view(|cx| ContextMenu::new(cx)),
|
context_menu: cx.add_view(|cx| ContextMenu::new(cx)),
|
||||||
};
|
};
|
||||||
this.update_visible_entries(None, cx);
|
this.update_visible_entries(None, cx);
|
||||||
|
@ -239,6 +258,11 @@ impl ProjectPanel {
|
||||||
menu_entries.push(ContextMenuItem::item("Copy", Copy));
|
menu_entries.push(ContextMenuItem::item("Copy", Copy));
|
||||||
menu_entries.push(ContextMenuItem::item("Copy Path", CopyPath));
|
menu_entries.push(ContextMenuItem::item("Copy Path", CopyPath));
|
||||||
menu_entries.push(ContextMenuItem::item("Cut", Cut));
|
menu_entries.push(ContextMenuItem::item("Cut", Cut));
|
||||||
|
if let Some(clipboard_entry) = self.clipboard_entry {
|
||||||
|
if clipboard_entry.worktree_id() == worktree.id() {
|
||||||
|
menu_entries.push(ContextMenuItem::item("Paste", Paste));
|
||||||
|
}
|
||||||
|
}
|
||||||
menu_entries.push(ContextMenuItem::Separator);
|
menu_entries.push(ContextMenuItem::Separator);
|
||||||
menu_entries.push(ContextMenuItem::item("Rename", Rename));
|
menu_entries.push(ContextMenuItem::item("Rename", Rename));
|
||||||
if !is_root {
|
if !is_root {
|
||||||
|
@ -608,15 +632,75 @@ impl ProjectPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
|
fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
|
||||||
todo!()
|
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
||||||
|
self.clipboard_entry = Some(ClipboardEntry::Cut {
|
||||||
|
worktree_id: worktree.id(),
|
||||||
|
entry_id: entry.id,
|
||||||
|
});
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
|
fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
|
||||||
todo!()
|
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
||||||
|
self.clipboard_entry = Some(ClipboardEntry::Copied {
|
||||||
|
worktree_id: worktree.id(),
|
||||||
|
entry_id: entry.id,
|
||||||
|
});
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
|
fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) -> Option<()> {
|
||||||
todo!()
|
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
||||||
|
let clipboard_entry = self.clipboard_entry?;
|
||||||
|
if clipboard_entry.worktree_id() != worktree.id() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let clipboard_entry_file_name = self
|
||||||
|
.project
|
||||||
|
.read(cx)
|
||||||
|
.path_for_entry(clipboard_entry.entry_id(), cx)?
|
||||||
|
.path
|
||||||
|
.file_name()?
|
||||||
|
.to_os_string();
|
||||||
|
|
||||||
|
let mut new_path = entry.path.to_path_buf();
|
||||||
|
if entry.is_file() {
|
||||||
|
new_path.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
new_path.push(&clipboard_entry_file_name);
|
||||||
|
let extension = new_path.extension().map(|e| e.to_os_string());
|
||||||
|
let file_name_without_extension = Path::new(&clipboard_entry_file_name).file_stem()?;
|
||||||
|
let mut ix = 0;
|
||||||
|
while worktree.entry_for_path(&new_path).is_some() {
|
||||||
|
new_path.pop();
|
||||||
|
|
||||||
|
let mut new_file_name = file_name_without_extension.to_os_string();
|
||||||
|
new_file_name.push(" copy");
|
||||||
|
if ix > 0 {
|
||||||
|
new_file_name.push(format!(" {}", ix));
|
||||||
|
}
|
||||||
|
new_path.push(new_file_name);
|
||||||
|
if let Some(extension) = extension.as_ref() {
|
||||||
|
new_path.set_extension(&extension);
|
||||||
|
}
|
||||||
|
ix += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.clipboard_entry.take();
|
||||||
|
if clipboard_entry.is_cut() {
|
||||||
|
self.project
|
||||||
|
.update(cx, |project, cx| {
|
||||||
|
project.rename_entry(clipboard_entry.entry_id(), new_path, cx)
|
||||||
|
})
|
||||||
|
.map(|task| task.detach_and_log_err(cx));
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
|
fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
|
||||||
|
@ -834,6 +918,9 @@ impl ProjectPanel {
|
||||||
}),
|
}),
|
||||||
is_editing: false,
|
is_editing: false,
|
||||||
is_processing: false,
|
is_processing: false,
|
||||||
|
is_cut: self
|
||||||
|
.clipboard_entry
|
||||||
|
.map_or(false, |e| e.is_cut() && e.entry_id() == entry.id),
|
||||||
};
|
};
|
||||||
if let Some(edit_state) = &self.edit_state {
|
if let Some(edit_state) = &self.edit_state {
|
||||||
let is_edited_entry = if edit_state.is_new_entry {
|
let is_edited_entry = if edit_state.is_new_entry {
|
||||||
|
@ -878,6 +965,10 @@ impl ProjectPanel {
|
||||||
style.text.color.fade_out(theme.ignored_entry_fade);
|
style.text.color.fade_out(theme.ignored_entry_fade);
|
||||||
style.icon_color.fade_out(theme.ignored_entry_fade);
|
style.icon_color.fade_out(theme.ignored_entry_fade);
|
||||||
}
|
}
|
||||||
|
if details.is_cut {
|
||||||
|
style.text.color.fade_out(theme.cut_entry_fade);
|
||||||
|
style.icon_color.fade_out(theme.cut_entry_fade);
|
||||||
|
}
|
||||||
let row_container_style = if show_editor {
|
let row_container_style = if show_editor {
|
||||||
theme.filename_editor.container
|
theme.filename_editor.container
|
||||||
} else {
|
} else {
|
||||||
|
@ -1018,6 +1109,27 @@ impl workspace::sidebar::SidebarItem for ProjectPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ClipboardEntry {
|
||||||
|
fn is_cut(&self) -> bool {
|
||||||
|
matches!(self, Self::Cut { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn entry_id(&self) -> ProjectEntryId {
|
||||||
|
match self {
|
||||||
|
ClipboardEntry::Copied { entry_id, .. } | ClipboardEntry::Cut { entry_id, .. } => {
|
||||||
|
*entry_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn worktree_id(&self) -> WorktreeId {
|
||||||
|
match self {
|
||||||
|
ClipboardEntry::Copied { worktree_id, .. }
|
||||||
|
| ClipboardEntry::Cut { worktree_id, .. } => *worktree_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -224,6 +224,7 @@ pub struct ProjectPanel {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub entry: Interactive<ProjectPanelEntry>,
|
pub entry: Interactive<ProjectPanelEntry>,
|
||||||
|
pub cut_entry_fade: f32,
|
||||||
pub ignored_entry_fade: f32,
|
pub ignored_entry_fade: f32,
|
||||||
pub filename_editor: FieldEditor,
|
pub filename_editor: FieldEditor,
|
||||||
pub indent_width: f32,
|
pub indent_width: f32,
|
||||||
|
|
|
@ -26,6 +26,7 @@ export default function projectPanel(theme: Theme) {
|
||||||
text: text(theme, "mono", "active", { size: "sm" }),
|
text: text(theme, "mono", "active", { size: "sm" }),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
cutEntryFade: 0.4,
|
||||||
ignoredEntryFade: 0.6,
|
ignoredEntryFade: 0.6,
|
||||||
filenameEditor: {
|
filenameEditor: {
|
||||||
background: backgroundColor(theme, 500, "active"),
|
background: backgroundColor(theme, 500, "active"),
|
||||||
|
|
Loading…
Reference in a new issue