project_panel: Add warning error for leading or trailing whitespace when creating file or directory (#28215)
- Show yellow warning (instead or error) for leading/trailing whitespace. - Do not block user from creating it. - If you rename existing file/dir which contains leading/trailing whitespace, it will show error right away. <img width="250" alt="image" src="https://github.com/user-attachments/assets/562895ee-3a86-4ecd-bb38-703d1d8b8599" /> Release Notes: - Added warning for leading or trailing whitespace while renaming or creating new file or directory in Project Panel.
This commit is contained in:
parent
3b46fca64c
commit
956f359045
1 changed files with 55 additions and 23 deletions
|
@ -119,6 +119,13 @@ struct FoldedDirectoryDragTarget {
|
||||||
is_delimiter_target: bool,
|
is_delimiter_target: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum ValidationState {
|
||||||
|
None,
|
||||||
|
Warning(String),
|
||||||
|
Error(String),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct EditState {
|
struct EditState {
|
||||||
worktree_id: WorktreeId,
|
worktree_id: WorktreeId,
|
||||||
|
@ -128,7 +135,7 @@ struct EditState {
|
||||||
depth: usize,
|
depth: usize,
|
||||||
processing_filename: Option<String>,
|
processing_filename: Option<String>,
|
||||||
previously_focused: Option<SelectedEntry>,
|
previously_focused: Option<SelectedEntry>,
|
||||||
validation_error: bool,
|
validation_state: ValidationState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditState {
|
impl EditState {
|
||||||
|
@ -1143,7 +1150,9 @@ impl ProjectPanel {
|
||||||
Some(state) => state,
|
Some(state) => state,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let filename = self.filename_editor.read(cx).text(cx);
|
let filename = self.filename_editor.read(cx).text(cx);
|
||||||
|
|
||||||
if !filename.is_empty() {
|
if !filename.is_empty() {
|
||||||
if let Some(worktree) = self
|
if let Some(worktree) = self
|
||||||
.project
|
.project
|
||||||
|
@ -1158,7 +1167,10 @@ impl ProjectPanel {
|
||||||
.entry_for_path(new_path.as_path())
|
.entry_for_path(new_path.as_path())
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
edit_state.validation_error = true;
|
edit_state.validation_state = ValidationState::Error(format!(
|
||||||
|
"File or directory '{}' already exists at location. Please choose a different name.",
|
||||||
|
filename
|
||||||
|
));
|
||||||
cx.notify();
|
cx.notify();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1171,7 +1183,9 @@ impl ProjectPanel {
|
||||||
if let Some(existing) = worktree.read(cx).entry_for_path(new_path.as_path())
|
if let Some(existing) = worktree.read(cx).entry_for_path(new_path.as_path())
|
||||||
{
|
{
|
||||||
if existing.id != entry.id {
|
if existing.id != entry.id {
|
||||||
edit_state.validation_error = true;
|
edit_state.validation_state = ValidationState::Error(
|
||||||
|
"File or directory already exists".to_string(),
|
||||||
|
);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1179,8 +1193,17 @@ impl ProjectPanel {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if filename.trim() != filename {
|
||||||
|
edit_state.validation_state = ValidationState::Warning(
|
||||||
|
"File or directory name contains leading or trailing whitespace.".to_string(),
|
||||||
|
);
|
||||||
|
cx.notify();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
edit_state.validation_error = false;
|
}
|
||||||
|
|
||||||
|
edit_state.validation_state = ValidationState::None;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1403,7 +1426,7 @@ impl ProjectPanel {
|
||||||
processing_filename: None,
|
processing_filename: None,
|
||||||
previously_focused: self.selection,
|
previously_focused: self.selection,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
validation_error: false,
|
validation_state: ValidationState::None,
|
||||||
});
|
});
|
||||||
self.filename_editor.update(cx, |editor, cx| {
|
self.filename_editor.update(cx, |editor, cx| {
|
||||||
editor.clear(window, cx);
|
editor.clear(window, cx);
|
||||||
|
@ -1453,7 +1476,7 @@ impl ProjectPanel {
|
||||||
processing_filename: None,
|
processing_filename: None,
|
||||||
previously_focused: None,
|
previously_focused: None,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
validation_error: false,
|
validation_state: ValidationState::None,
|
||||||
});
|
});
|
||||||
let file_name = entry
|
let file_name = entry
|
||||||
.path
|
.path
|
||||||
|
@ -3687,15 +3710,25 @@ impl ProjectPanel {
|
||||||
item_colors.hover
|
item_colors.hover
|
||||||
};
|
};
|
||||||
|
|
||||||
let validation_error =
|
let validation_color_and_message = if show_editor {
|
||||||
show_editor && self.edit_state.as_ref().is_some_and(|e| e.validation_error);
|
match self
|
||||||
|
.edit_state
|
||||||
|
.as_ref()
|
||||||
|
.map_or(ValidationState::None, |e| e.validation_state.clone())
|
||||||
|
{
|
||||||
|
ValidationState::Error(msg) => Some((Color::Error.color(cx), msg.clone())),
|
||||||
|
ValidationState::Warning(msg) => Some((Color::Warning.color(cx), msg.clone())),
|
||||||
|
ValidationState::None => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let border_color =
|
let border_color =
|
||||||
if !self.mouse_down && is_active && self.focus_handle.contains_focused(window, cx) {
|
if !self.mouse_down && is_active && self.focus_handle.contains_focused(window, cx) {
|
||||||
if validation_error {
|
match validation_color_and_message {
|
||||||
Color::Error.color(cx)
|
Some((color, _)) => color,
|
||||||
} else {
|
None => item_colors.focused,
|
||||||
item_colors.focused
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bg_color
|
bg_color
|
||||||
|
@ -3703,10 +3736,9 @@ impl ProjectPanel {
|
||||||
|
|
||||||
let border_hover_color =
|
let border_hover_color =
|
||||||
if !self.mouse_down && is_active && self.focus_handle.contains_focused(window, cx) {
|
if !self.mouse_down && is_active && self.focus_handle.contains_focused(window, cx) {
|
||||||
if validation_error {
|
match validation_color_and_message {
|
||||||
Color::Error.color(cx)
|
Some((color, _)) => color,
|
||||||
} else {
|
None => item_colors.focused,
|
||||||
item_colors.focused
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bg_hover_color
|
bg_hover_color
|
||||||
|
@ -4177,8 +4209,9 @@ impl ProjectPanel {
|
||||||
))
|
))
|
||||||
.overflow_x(),
|
.overflow_x(),
|
||||||
)
|
)
|
||||||
.when(
|
.when_some(
|
||||||
validation_error, |this| {
|
validation_color_and_message,
|
||||||
|
|this, (color, message)| {
|
||||||
this
|
this
|
||||||
.relative()
|
.relative()
|
||||||
.child(
|
.child(
|
||||||
|
@ -4192,13 +4225,12 @@ impl ProjectPanel {
|
||||||
.py_1()
|
.py_1()
|
||||||
.px_2()
|
.px_2()
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(Color::Error.color(cx))
|
.border_color(color)
|
||||||
.bg(cx.theme().colors().background)
|
.bg(cx.theme().colors().background)
|
||||||
.child(
|
.child(
|
||||||
Label::new(format!("{} already exists", self.filename_editor.read(cx).text(cx)))
|
Label::new(message)
|
||||||
.color(Color::Error)
|
.color(Color::from(color))
|
||||||
.size(LabelSize::Small)
|
.size(LabelSize::Small)
|
||||||
.truncate()
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue