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:
Smit Barmase 2025-04-07 17:47:54 +05:30 committed by GitHub
parent 3b46fca64c
commit 956f359045
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -119,6 +119,13 @@ struct FoldedDirectoryDragTarget {
is_delimiter_target: bool,
}
#[derive(Clone, Debug)]
enum ValidationState {
None,
Warning(String),
Error(String),
}
#[derive(Clone, Debug)]
struct EditState {
worktree_id: WorktreeId,
@ -128,7 +135,7 @@ struct EditState {
depth: usize,
processing_filename: Option<String>,
previously_focused: Option<SelectedEntry>,
validation_error: bool,
validation_state: ValidationState,
}
impl EditState {
@ -1143,7 +1150,9 @@ impl ProjectPanel {
Some(state) => state,
None => return,
};
let filename = self.filename_editor.read(cx).text(cx);
if !filename.is_empty() {
if let Some(worktree) = self
.project
@ -1158,7 +1167,10 @@ impl ProjectPanel {
.entry_for_path(new_path.as_path())
.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();
return;
}
@ -1171,7 +1183,9 @@ impl ProjectPanel {
if let Some(existing) = worktree.read(cx).entry_for_path(new_path.as_path())
{
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();
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();
}
@ -1403,7 +1426,7 @@ impl ProjectPanel {
processing_filename: None,
previously_focused: self.selection,
depth: 0,
validation_error: false,
validation_state: ValidationState::None,
});
self.filename_editor.update(cx, |editor, cx| {
editor.clear(window, cx);
@ -1453,7 +1476,7 @@ impl ProjectPanel {
processing_filename: None,
previously_focused: None,
depth: 0,
validation_error: false,
validation_state: ValidationState::None,
});
let file_name = entry
.path
@ -3687,15 +3710,25 @@ impl ProjectPanel {
item_colors.hover
};
let validation_error =
show_editor && self.edit_state.as_ref().is_some_and(|e| e.validation_error);
let validation_color_and_message = if show_editor {
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 =
if !self.mouse_down && is_active && self.focus_handle.contains_focused(window, cx) {
if validation_error {
Color::Error.color(cx)
} else {
item_colors.focused
match validation_color_and_message {
Some((color, _)) => color,
None => item_colors.focused,
}
} else {
bg_color
@ -3703,10 +3736,9 @@ impl ProjectPanel {
let border_hover_color =
if !self.mouse_down && is_active && self.focus_handle.contains_focused(window, cx) {
if validation_error {
Color::Error.color(cx)
} else {
item_colors.focused
match validation_color_and_message {
Some((color, _)) => color,
None => item_colors.focused,
}
} else {
bg_hover_color
@ -4177,9 +4209,10 @@ impl ProjectPanel {
))
.overflow_x(),
)
.when(
validation_error, |this| {
this
.when_some(
validation_color_and_message,
|this, (color, message)| {
this
.relative()
.child(
deferred(
@ -4192,13 +4225,12 @@ impl ProjectPanel {
.py_1()
.px_2()
.border_1()
.border_color(Color::Error.color(cx))
.border_color(color)
.bg(cx.theme().colors().background)
.child(
Label::new(format!("{} already exists", self.filename_editor.read(cx).text(cx)))
.color(Color::Error)
Label::new(message)
.color(Color::from(color))
.size(LabelSize::Small)
.truncate()
)
)
)