From 2ad7ecbcf02541d3a8eeecffbdff649f998d26a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20C=C3=A1rdenas?= Date: Tue, 11 Nov 2025 16:53:40 -0500 Subject: [PATCH] project_panel: Add `auto_open` settings (#40435) - Based on #40234, and improvement of #40331 Release Notes: - Added granular settings to control when files auto-open in the project panel (project_panel.auto_open.on_create, on_paste, on_drop) Screenshot_2025-10-16_17-28-31 --------- Co-authored-by: Smit Barmase --- Cargo.lock | 1 + assets/settings/default.json | 11 +- crates/migrator/src/migrations.rs | 6 + .../src/migrations/m_2025_11_12/settings.rs | 84 ++++++ crates/migrator/src/migrator.rs | 53 ++++ crates/project_panel/Cargo.toml | 1 + crates/project_panel/src/project_panel.rs | 29 +- .../src/project_panel_settings.rs | 35 ++- .../project_panel/src/project_panel_tests.rs | 257 +++++++++++++++++- .../src/settings_content/workspace.rs | 23 +- crates/settings/src/vscode_import.rs | 2 +- crates/settings_ui/src/page_data.rs | 48 +++- docs/src/configuring-zed.md | 26 +- 13 files changed, 542 insertions(+), 34 deletions(-) create mode 100644 crates/migrator/src/migrations/m_2025_11_12/settings.rs diff --git a/Cargo.lock b/Cargo.lock index a3300a818c12f39406cc39848cae86eeb26a0a56..865dfe211ef606f4469be6017129dfac2916522b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13078,6 +13078,7 @@ dependencies = [ "settings", "smallvec", "telemetry", + "tempfile", "theme", "ui", "util", diff --git a/assets/settings/default.json b/assets/settings/default.json index d8c800081246dcf937f7380399d726dd3d349679..70011f3209c7f64fd4e86d3acbb62a9ff2d5a487 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -748,8 +748,15 @@ "hide_root": false, // Whether to hide the hidden entries in the project panel. "hide_hidden": false, - // Whether to automatically open files when pasting them in the project panel. - "open_file_on_paste": true + // Settings for automatically opening files. + "auto_open": { + // Whether to automatically open newly created files in the editor. + "on_create": true, + // Whether to automatically open files after pasting or duplicating them. + "on_paste": true, + // Whether to automatically open files dropped from external sources. + "on_drop": true + } }, "outline_panel": { // Whether to show the outline panel button in the status bar diff --git a/crates/migrator/src/migrations.rs b/crates/migrator/src/migrations.rs index e4358b36b94c9a738ad784eb7269652b29e7cdfb..2587e7a30829d4fa0e0832b91ab0294a86abc97e 100644 --- a/crates/migrator/src/migrations.rs +++ b/crates/migrator/src/migrations.rs @@ -135,3 +135,9 @@ pub(crate) mod m_2025_10_21 { pub(crate) use settings::make_relative_line_numbers_an_enum; } + +pub(crate) mod m_2025_11_12 { + mod settings; + + pub(crate) use settings::SETTINGS_PATTERNS; +} diff --git a/crates/migrator/src/migrations/m_2025_11_12/settings.rs b/crates/migrator/src/migrations/m_2025_11_12/settings.rs new file mode 100644 index 0000000000000000000000000000000000000000..6483f9e44bec64407313334e7b78d181e7a05815 --- /dev/null +++ b/crates/migrator/src/migrations/m_2025_11_12/settings.rs @@ -0,0 +1,84 @@ +use std::ops::Range; +use tree_sitter::{Query, QueryMatch}; + +use crate::MigrationPatterns; +use crate::patterns::SETTINGS_NESTED_KEY_VALUE_PATTERN; + +pub const SETTINGS_PATTERNS: MigrationPatterns = &[ + ( + SETTINGS_NESTED_KEY_VALUE_PATTERN, + rename_open_file_on_paste_setting, + ), + ( + SETTINGS_NESTED_KEY_VALUE_PATTERN, + replace_open_file_on_paste_setting_value, + ), +]; + +fn rename_open_file_on_paste_setting( + contents: &str, + mat: &QueryMatch, + query: &Query, +) -> Option<(Range, String)> { + if !is_project_panel_open_file_on_paste(contents, mat, query) { + return None; + } + + let setting_name_ix = query.capture_index_for_name("setting_name")?; + let setting_name_range = mat + .nodes_for_capture_index(setting_name_ix) + .next()? + .byte_range(); + + Some((setting_name_range, "auto_open".to_string())) +} + +fn replace_open_file_on_paste_setting_value( + contents: &str, + mat: &QueryMatch, + query: &Query, +) -> Option<(Range, String)> { + if !is_project_panel_open_file_on_paste(contents, mat, query) { + return None; + } + + let value_ix = query.capture_index_for_name("setting_value")?; + let value_node = mat.nodes_for_capture_index(value_ix).next()?; + let value_range = value_node.byte_range(); + let value_text = contents.get(value_range.clone())?.trim(); + + let normalized_value = match value_text { + "true" => "true", + "false" => "false", + _ => return None, + }; + + Some(( + value_range, + format!("{{ \"on_paste\": {normalized_value} }}"), + )) +} + +fn is_project_panel_open_file_on_paste(contents: &str, mat: &QueryMatch, query: &Query) -> bool { + let parent_key_ix = match query.capture_index_for_name("parent_key") { + Some(ix) => ix, + None => return false, + }; + let parent_range = match mat.nodes_for_capture_index(parent_key_ix).next() { + Some(node) => node.byte_range(), + None => return false, + }; + if contents.get(parent_range) != Some("project_panel") { + return false; + } + + let setting_name_ix = match query.capture_index_for_name("setting_name") { + Some(ix) => ix, + None => return false, + }; + let setting_name_range = match mat.nodes_for_capture_index(setting_name_ix).next() { + Some(node) => node.byte_range(), + None => return false, + }; + contents.get(setting_name_range) == Some("open_file_on_paste") +} diff --git a/crates/migrator/src/migrator.rs b/crates/migrator/src/migrator.rs index 3f5c1edaa7939e442c3e5c007579516fcdeb2151..74b73114cae81b57e5d0dc4227bafcd2cca31d10 100644 --- a/crates/migrator/src/migrator.rs +++ b/crates/migrator/src/migrator.rs @@ -215,6 +215,10 @@ pub fn migrate_settings(text: &str) -> Result> { MigrationType::Json(migrations::m_2025_10_16::restore_code_actions_on_format), MigrationType::Json(migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum), MigrationType::Json(migrations::m_2025_10_21::make_relative_line_numbers_an_enum), + MigrationType::TreeSitter( + migrations::m_2025_11_12::SETTINGS_PATTERNS, + &SETTINGS_QUERY_2025_11_12, + ), ]; run_migrations(text, migrations) } @@ -333,6 +337,10 @@ define_query!( SETTINGS_QUERY_2025_10_03, migrations::m_2025_10_03::SETTINGS_PATTERNS ); +define_query!( + SETTINGS_QUERY_2025_11_12, + migrations::m_2025_11_12::SETTINGS_PATTERNS +); // custom query static EDIT_PREDICTION_SETTINGS_MIGRATION_QUERY: LazyLock = LazyLock::new(|| { @@ -2193,4 +2201,49 @@ mod tests { ), ); } + + #[test] + fn test_project_panel_open_file_on_paste_migration() { + assert_migrate_settings( + &r#" + { + "project_panel": { + "open_file_on_paste": true + } + } + "# + .unindent(), + Some( + &r#" + { + "project_panel": { + "auto_open": { "on_paste": true } + } + } + "# + .unindent(), + ), + ); + + assert_migrate_settings( + &r#" + { + "project_panel": { + "open_file_on_paste": false + } + } + "# + .unindent(), + Some( + &r#" + { + "project_panel": { + "auto_open": { "on_paste": false } + } + } + "# + .unindent(), + ), + ); + } } diff --git a/crates/project_panel/Cargo.toml b/crates/project_panel/Cargo.toml index a1238990db8617977494d151b1ab9e46a17d715f..2c47efd0b0e2490bbfd6125069fa5ca1438ffb51 100644 --- a/crates/project_panel/Cargo.toml +++ b/crates/project_panel/Cargo.toml @@ -53,4 +53,5 @@ editor = { workspace = true, features = ["test-support"] } gpui = { workspace = true, features = ["test-support"] } language = { workspace = true, features = ["test-support"] } serde_json.workspace = true +tempfile.workspace = true workspace = { workspace = true, features = ["test-support"] } diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 38fd1d08c9802bd04c7e5faf60c171d492ed996f..8830de5aeffcd26b0f5c342fc1c8d16cdb762b40 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1655,7 +1655,10 @@ impl ProjectPanel { } project_panel.update_visible_entries(None, false, false, window, cx); if is_new_entry && !is_dir { - project_panel.open_entry(new_entry.id, true, false, cx); + let settings = ProjectPanelSettings::get_global(cx); + if settings.auto_open.should_open_on_create() { + project_panel.open_entry(new_entry.id, true, false, cx); + } } cx.notify(); })?; @@ -2709,15 +2712,16 @@ impl ProjectPanel { if item_count == 1 { // open entry if not dir, setting is enabled, and only focus if rename is not pending - if !entry.is_dir() - && ProjectPanelSettings::get_global(cx).open_file_on_paste - { - project_panel.open_entry( - entry.id, - disambiguation_range.is_none(), - false, - cx, - ); + if !entry.is_dir() { + let settings = ProjectPanelSettings::get_global(cx); + if settings.auto_open.should_open_on_paste() { + project_panel.open_entry( + entry.id, + disambiguation_range.is_none(), + false, + cx, + ); + } } // if only one entry was pasted and it was disambiguated, open the rename editor @@ -3593,7 +3597,10 @@ impl ProjectPanel { let opened_entries = task.await.with_context(|| "failed to copy external paths")?; this.update(cx, |this, cx| { if open_file_after_drop && !opened_entries.is_empty() { - this.open_entry(opened_entries[0], true, false, cx); + let settings = ProjectPanelSettings::get_global(cx); + if settings.auto_open.should_open_on_drop() { + this.open_entry(opened_entries[0], true, false, cx); + } } }) } diff --git a/crates/project_panel/src/project_panel_settings.rs b/crates/project_panel/src/project_panel_settings.rs index 623fdda310ec872ee3919cb944d8f1f817d10448..266ab761a103fa4ca2a2e9a4e09b96514bfd25c1 100644 --- a/crates/project_panel/src/project_panel_settings.rs +++ b/crates/project_panel/src/project_panel_settings.rs @@ -32,7 +32,7 @@ pub struct ProjectPanelSettings { pub hide_root: bool, pub hide_hidden: bool, pub drag_and_drop: bool, - pub open_file_on_paste: bool, + pub auto_open: AutoOpenSettings, } #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] @@ -48,6 +48,30 @@ pub struct ScrollbarSettings { pub show: Option, } +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct AutoOpenSettings { + pub on_create: bool, + pub on_paste: bool, + pub on_drop: bool, +} + +impl AutoOpenSettings { + #[inline] + pub fn should_open_on_create(self) -> bool { + self.on_create + } + + #[inline] + pub fn should_open_on_paste(self) -> bool { + self.on_paste + } + + #[inline] + pub fn should_open_on_drop(self) -> bool { + self.on_drop + } +} + impl ScrollbarVisibility for ProjectPanelSettings { fn visibility(&self, cx: &ui::App) -> ShowScrollbar { self.scrollbar @@ -83,7 +107,14 @@ impl Settings for ProjectPanelSettings { hide_root: project_panel.hide_root.unwrap(), hide_hidden: project_panel.hide_hidden.unwrap(), drag_and_drop: project_panel.drag_and_drop.unwrap(), - open_file_on_paste: project_panel.open_file_on_paste.unwrap(), + auto_open: { + let auto_open = project_panel.auto_open.unwrap(); + AutoOpenSettings { + on_create: auto_open.on_create.unwrap(), + on_paste: auto_open.on_paste.unwrap(), + on_drop: auto_open.on_drop.unwrap(), + } + }, } } } diff --git a/crates/project_panel/src/project_panel_tests.rs b/crates/project_panel/src/project_panel_tests.rs index 51f028afd5ac07c15e55f15d68f75293fab3481a..675ed9c35208917aa80002d9daa7932f92a29495 100644 --- a/crates/project_panel/src/project_panel_tests.rs +++ b/crates/project_panel/src/project_panel_tests.rs @@ -4,7 +4,7 @@ use gpui::{Empty, Entity, TestAppContext, VisualTestContext, WindowHandle}; use pretty_assertions::assert_eq; use project::FakeFs; use serde_json::json; -use settings::SettingsStore; +use settings::{ProjectPanelAutoOpenSettings, SettingsStore}; use std::path::{Path, PathBuf}; use util::{path, paths::PathStyle, rel_path::rel_path}; use workspace::{ @@ -1998,6 +1998,248 @@ async fn test_remove_opened_file(cx: &mut gpui::TestAppContext) { ensure_no_open_items_and_panes(&workspace, cx); } +#[gpui::test] +async fn test_auto_open_new_file_when_enabled(cx: &mut gpui::TestAppContext) { + init_test_with_editor(cx); + set_auto_open_settings( + cx, + ProjectPanelAutoOpenSettings { + on_create: Some(true), + ..Default::default() + }, + ); + + let fs = FakeFs::new(cx.executor()); + fs.insert_tree(path!("/root"), json!({})).await; + + let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; + let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); + let cx = &mut VisualTestContext::from_window(*workspace, cx); + let panel = workspace.update(cx, ProjectPanel::new).unwrap(); + cx.run_until_parked(); + + panel.update_in(cx, |panel, window, cx| panel.new_file(&NewFile, window, cx)); + cx.run_until_parked(); + panel + .update_in(cx, |panel, window, cx| { + panel.filename_editor.update(cx, |editor, cx| { + editor.set_text("auto-open.rs", window, cx); + }); + panel.confirm_edit(true, window, cx).unwrap() + }) + .await + .unwrap(); + cx.run_until_parked(); + + ensure_single_file_is_opened(&workspace, "auto-open.rs", cx); +} + +#[gpui::test] +async fn test_auto_open_new_file_when_disabled(cx: &mut gpui::TestAppContext) { + init_test_with_editor(cx); + set_auto_open_settings( + cx, + ProjectPanelAutoOpenSettings { + on_create: Some(false), + ..Default::default() + }, + ); + + let fs = FakeFs::new(cx.executor()); + fs.insert_tree(path!("/root"), json!({})).await; + + let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; + let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); + let cx = &mut VisualTestContext::from_window(*workspace, cx); + let panel = workspace.update(cx, ProjectPanel::new).unwrap(); + cx.run_until_parked(); + + panel.update_in(cx, |panel, window, cx| panel.new_file(&NewFile, window, cx)); + cx.run_until_parked(); + panel + .update_in(cx, |panel, window, cx| { + panel.filename_editor.update(cx, |editor, cx| { + editor.set_text("manual-open.rs", window, cx); + }); + panel.confirm_edit(true, window, cx).unwrap() + }) + .await + .unwrap(); + cx.run_until_parked(); + + ensure_no_open_items_and_panes(&workspace, cx); +} + +#[gpui::test] +async fn test_auto_open_on_paste_when_enabled(cx: &mut gpui::TestAppContext) { + init_test_with_editor(cx); + set_auto_open_settings( + cx, + ProjectPanelAutoOpenSettings { + on_paste: Some(true), + ..Default::default() + }, + ); + + let fs = FakeFs::new(cx.executor()); + fs.insert_tree( + path!("/root"), + json!({ + "src": { + "original.rs": "" + }, + "target": {} + }), + ) + .await; + + let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; + let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); + let cx = &mut VisualTestContext::from_window(*workspace, cx); + let panel = workspace.update(cx, ProjectPanel::new).unwrap(); + cx.run_until_parked(); + + toggle_expand_dir(&panel, "root/src", cx); + toggle_expand_dir(&panel, "root/target", cx); + + select_path(&panel, "root/src/original.rs", cx); + panel.update_in(cx, |panel, window, cx| { + panel.copy(&Default::default(), window, cx); + }); + + select_path(&panel, "root/target", cx); + panel.update_in(cx, |panel, window, cx| { + panel.paste(&Default::default(), window, cx); + }); + cx.executor().run_until_parked(); + + ensure_single_file_is_opened(&workspace, "target/original.rs", cx); +} + +#[gpui::test] +async fn test_auto_open_on_paste_when_disabled(cx: &mut gpui::TestAppContext) { + init_test_with_editor(cx); + set_auto_open_settings( + cx, + ProjectPanelAutoOpenSettings { + on_paste: Some(false), + ..Default::default() + }, + ); + + let fs = FakeFs::new(cx.executor()); + fs.insert_tree( + path!("/root"), + json!({ + "src": { + "original.rs": "" + }, + "target": {} + }), + ) + .await; + + let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; + let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); + let cx = &mut VisualTestContext::from_window(*workspace, cx); + let panel = workspace.update(cx, ProjectPanel::new).unwrap(); + cx.run_until_parked(); + + toggle_expand_dir(&panel, "root/src", cx); + toggle_expand_dir(&panel, "root/target", cx); + + select_path(&panel, "root/src/original.rs", cx); + panel.update_in(cx, |panel, window, cx| { + panel.copy(&Default::default(), window, cx); + }); + + select_path(&panel, "root/target", cx); + panel.update_in(cx, |panel, window, cx| { + panel.paste(&Default::default(), window, cx); + }); + cx.executor().run_until_parked(); + + ensure_no_open_items_and_panes(&workspace, cx); + assert!( + find_project_entry(&panel, "root/target/original.rs", cx).is_some(), + "Pasted entry should exist even when auto-open is disabled" + ); +} + +#[gpui::test] +async fn test_auto_open_on_drop_when_enabled(cx: &mut gpui::TestAppContext) { + init_test_with_editor(cx); + set_auto_open_settings( + cx, + ProjectPanelAutoOpenSettings { + on_drop: Some(true), + ..Default::default() + }, + ); + + let fs = FakeFs::new(cx.executor()); + fs.insert_tree(path!("/root"), json!({})).await; + + let temp_dir = tempfile::tempdir().unwrap(); + let external_path = temp_dir.path().join("dropped.rs"); + std::fs::write(&external_path, "// dropped").unwrap(); + fs.insert_tree_from_real_fs(temp_dir.path(), temp_dir.path()) + .await; + + let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; + let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); + let cx = &mut VisualTestContext::from_window(*workspace, cx); + let panel = workspace.update(cx, ProjectPanel::new).unwrap(); + cx.run_until_parked(); + + let root_entry = find_project_entry(&panel, "root", cx).unwrap(); + panel.update_in(cx, |panel, window, cx| { + panel.drop_external_files(std::slice::from_ref(&external_path), root_entry, window, cx); + }); + cx.executor().run_until_parked(); + + ensure_single_file_is_opened(&workspace, "dropped.rs", cx); +} + +#[gpui::test] +async fn test_auto_open_on_drop_when_disabled(cx: &mut gpui::TestAppContext) { + init_test_with_editor(cx); + set_auto_open_settings( + cx, + ProjectPanelAutoOpenSettings { + on_drop: Some(false), + ..Default::default() + }, + ); + + let fs = FakeFs::new(cx.executor()); + fs.insert_tree(path!("/root"), json!({})).await; + + let temp_dir = tempfile::tempdir().unwrap(); + let external_path = temp_dir.path().join("manual.rs"); + std::fs::write(&external_path, "// dropped").unwrap(); + fs.insert_tree_from_real_fs(temp_dir.path(), temp_dir.path()) + .await; + + let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; + let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); + let cx = &mut VisualTestContext::from_window(*workspace, cx); + let panel = workspace.update(cx, ProjectPanel::new).unwrap(); + cx.run_until_parked(); + + let root_entry = find_project_entry(&panel, "root", cx).unwrap(); + panel.update_in(cx, |panel, window, cx| { + panel.drop_external_files(std::slice::from_ref(&external_path), root_entry, window, cx); + }); + cx.executor().run_until_parked(); + + ensure_no_open_items_and_panes(&workspace, cx); + assert!( + find_project_entry(&panel, "root/manual.rs", cx).is_some(), + "Dropped entry should exist even when auto-open is disabled" + ); +} + #[gpui::test] async fn test_create_duplicate_items(cx: &mut gpui::TestAppContext) { init_test_with_editor(cx); @@ -7368,6 +7610,19 @@ fn init_test_with_editor(cx: &mut TestAppContext) { }); } +fn set_auto_open_settings( + cx: &mut TestAppContext, + auto_open_settings: ProjectPanelAutoOpenSettings, +) { + cx.update(|cx| { + cx.update_global::(|store, cx| { + store.update_user_settings(cx, |settings| { + settings.project_panel.get_or_insert_default().auto_open = Some(auto_open_settings); + }); + }) + }); +} + fn ensure_single_file_is_opened( window: &WindowHandle, expected_path: &str, diff --git a/crates/settings/src/settings_content/workspace.rs b/crates/settings/src/settings_content/workspace.rs index c901d7010b37c685180ca67a3c4775da41be87ee..01c40528cb4a9b614270efbbf0d39b1b424bb7dc 100644 --- a/crates/settings/src/settings_content/workspace.rs +++ b/crates/settings/src/settings_content/workspace.rs @@ -510,6 +510,23 @@ impl OnLastWindowClosed { } } +#[skip_serializing_none] +#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)] +pub struct ProjectPanelAutoOpenSettings { + /// Whether to automatically open newly created files in the editor. + /// + /// Default: true + pub on_create: Option, + /// Whether to automatically open files after pasting or duplicating them. + /// + /// Default: true + pub on_paste: Option, + /// Whether to automatically open files dropped from external sources. + /// + /// Default: true + pub on_drop: Option, +} + #[skip_serializing_none] #[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)] pub struct ProjectPanelSettingsContent { @@ -590,10 +607,8 @@ pub struct ProjectPanelSettingsContent { /// /// Default: true pub drag_and_drop: Option, - /// Whether to automatically open files when pasting them in the project panel. - /// - /// Default: true - pub open_file_on_paste: Option, + /// Settings for automatically opening files. + pub auto_open: Option, } #[derive( diff --git a/crates/settings/src/vscode_import.rs b/crates/settings/src/vscode_import.rs index cbffb33b1795dbf71e48df8089c472ee534306c1..31f1ab82b50b5fca32203c770cd41795e1cf92c3 100644 --- a/crates/settings/src/vscode_import.rs +++ b/crates/settings/src/vscode_import.rs @@ -664,13 +664,13 @@ impl VsCodeSettings { hide_root: None, indent_guides: None, indent_size: None, - open_file_on_paste: None, scrollbar: None, show_diagnostics: self .read_bool("problems.decorations.enabled") .and_then(|b| if b { Some(ShowDiagnostics::Off) } else { None }), starts_open: None, sticky_scroll: None, + auto_open: None, }; if let (Some(false), Some(false)) = ( diff --git a/crates/settings_ui/src/page_data.rs b/crates/settings_ui/src/page_data.rs index e3165fbc79850484950e90bdcdbb81338df9974d..973f40a20a5cc6052f30ba2ff17a5116c96eeb2b 100644 --- a/crates/settings_ui/src/page_data.rs +++ b/crates/settings_ui/src/page_data.rs @@ -3776,23 +3776,47 @@ pub(crate) fn settings_data(cx: &App) -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SectionHeader("Auto Open Files"), SettingsPageItem::SettingItem(SettingItem { - title: "Open File on Paste", - description: "Whether to automatically open files when pasting them in the project panel.", + title: "On Create", + description: "Whether to automatically open newly created files in the editor.", field: Box::new(SettingField { - json_path: Some("project_panel.open_file_on_paste"), + json_path: Some("project_panel.auto_open.on_create"), pick: |settings_content| { - settings_content - .project_panel - .as_ref()? - .open_file_on_paste - .as_ref() + settings_content.project_panel.as_ref()?.auto_open.as_ref()?.on_create.as_ref() }, write: |settings_content, value| { - settings_content - .project_panel - .get_or_insert_default() - .open_file_on_paste = value; + settings_content.project_panel.get_or_insert_default().auto_open.get_or_insert_default().on_create = value; + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "On Paste", + description: "Whether to automatically open files after pasting or duplicating them.", + field: Box::new(SettingField { + json_path: Some("project_panel.auto_open.on_paste"), + pick: |settings_content| { + settings_content.project_panel.as_ref()?.auto_open.as_ref()?.on_paste.as_ref() + }, + write: |settings_content, value| { + settings_content.project_panel.get_or_insert_default().auto_open.get_or_insert_default().on_paste = value; + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "On Drop", + description: "Whether to automatically open files dropped from external sources.", + field: Box::new(SettingField { + json_path: Some("project_panel.auto_open.on_drop"), + pick: |settings_content| { + settings_content.project_panel.as_ref()?.auto_open.as_ref()?.on_drop.as_ref() + }, + write: |settings_content, value| { + settings_content.project_panel.get_or_insert_default().auto_open.get_or_insert_default().on_drop = value; }, }), metadata: None, diff --git a/docs/src/configuring-zed.md b/docs/src/configuring-zed.md index 07d93fd6d167bafeb0a8e4bc72f80f52265edee1..6841c9a3cb0364d8eab63a9319df2e6a38d5612e 100644 --- a/docs/src/configuring-zed.md +++ b/docs/src/configuring-zed.md @@ -4280,7 +4280,11 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a "hide_root": false, "hide_hidden": false, "starts_open": true, - "open_file_on_paste": true + "auto_open": { + "on_create": true, + "on_paste": true, + "on_drop": true + } } } ``` @@ -4489,6 +4493,26 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a } ``` +### Auto Open + +- Description: Control whether files are opened automatically after different creation flows in the project panel. +- Setting: `auto_open` +- Default: + +```json [settings] +"auto_open": { + "on_create": true, + "on_paste": true, + "on_drop": true +} +``` + +**Options** + +- `on_create`: Whether to automatically open newly created files in the editor. +- `on_paste`: Whether to automatically open files after pasting or duplicating them. +- `on_drop`: Whether to automatically open files dropped from external sources. + ## Agent Visit [the Configuration page](./ai/configuration.md) under the AI section to learn more about all the agent-related settings.