diff --git a/assets/settings/default.json b/assets/settings/default.json index dc1040e1d0..8c105b2c1e 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -228,7 +228,12 @@ // Whether to show code action button at start of buffer line. "inline_code_actions": true, // Whether to allow drag and drop text selection in buffer. - "drag_and_drop_selection": true, + "drag_and_drop_selection": { + // When true, enables drag and drop text selection in buffer. + "enabled": true, + // The delay in milliseconds that must elapse before drag and drop is allowed. Otherwise, a new text selection is created. + "delay": 300 + }, // What to do when go to definition yields no results. // // 1. Do nothing: `none` diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 03e2124742..c5fe0db74c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1170,7 +1170,6 @@ pub struct Editor { pub change_list: ChangeList, inline_value_cache: InlineValueCache, selection_drag_state: SelectionDragState, - drag_and_drop_selection_enabled: bool, next_color_inlay_id: usize, colors: Option, folding_newlines: Task<()>, @@ -2202,7 +2201,6 @@ impl Editor { change_list: ChangeList::new(), mode, selection_drag_state: SelectionDragState::None, - drag_and_drop_selection_enabled: EditorSettings::get_global(cx).drag_and_drop_selection, folding_newlines: Task::ready(()), }; if let Some(breakpoints) = editor.breakpoint_store.as_ref() { @@ -19899,7 +19897,6 @@ impl Editor { self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs; self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default(); self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default(); - self.drag_and_drop_selection_enabled = editor_settings.drag_and_drop_selection; } if old_cursor_shape != self.cursor_shape { diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index d7b8bac359..5d8379ddfb 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -52,7 +52,7 @@ pub struct EditorSettings { #[serde(default)] pub diagnostics_max_severity: Option, pub inline_code_actions: bool, - pub drag_and_drop_selection: bool, + pub drag_and_drop_selection: DragAndDropSelection, pub lsp_document_colors: DocumentColorsRenderMode, } @@ -275,6 +275,26 @@ pub struct ScrollbarAxes { pub vertical: bool, } +/// Whether to allow drag and drop text selection in buffer. +#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct DragAndDropSelection { + /// When true, enables drag and drop text selection in buffer. + /// + /// Default: true + #[serde(default = "default_true")] + pub enabled: bool, + + /// The delay in milliseconds that must elapse before drag and drop is allowed. Otherwise, a new text selection is created. + /// + /// Default: 300 + #[serde(default = "default_drag_and_drop_selection_delay_ms")] + pub delay: u64, +} + +fn default_drag_and_drop_selection_delay_ms() -> u64 { + 300 +} + /// Which diagnostic indicators to show in the scrollbar. /// /// Default: all @@ -536,10 +556,8 @@ pub struct EditorSettingsContent { /// Default: true pub inline_code_actions: Option, - /// Whether to allow drag and drop text selection in buffer. - /// - /// Default: true - pub drag_and_drop_selection: Option, + /// Drag and drop related settings + pub drag_and_drop_selection: Option, /// How to render LSP `textDocument/documentColor` colors in the editor. /// diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a446338335..8a5bfb3bab 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -87,7 +87,6 @@ use util::{RangeExt, ResultExt, debug_panic}; use workspace::{CollaboratorId, Workspace, item::Item, notifications::NotifyTaskExt}; const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 7.; -const SELECTION_DRAG_DELAY: Duration = Duration::from_millis(300); /// Determines what kinds of highlights should be applied to a lines background. #[derive(Clone, Copy, Default)] @@ -644,7 +643,11 @@ impl EditorElement { return; } - if editor.drag_and_drop_selection_enabled && click_count == 1 { + if EditorSettings::get_global(cx) + .drag_and_drop_selection + .enabled + && click_count == 1 + { let newest_anchor = editor.selections.newest_anchor(); let snapshot = editor.snapshot(window, cx); let selection = newest_anchor.map(|anchor| anchor.to_display_point(&snapshot)); @@ -1022,7 +1025,10 @@ impl EditorElement { ref click_position, ref mouse_down_time, } => { - if mouse_down_time.elapsed() >= SELECTION_DRAG_DELAY { + let drag_and_drop_delay = Duration::from_millis( + EditorSettings::get_global(cx).drag_and_drop_selection.delay, + ); + if mouse_down_time.elapsed() >= drag_and_drop_delay { let drop_cursor = Selection { id: post_inc(&mut editor.selections.next_selection_id), start: drop_anchor, @@ -5710,6 +5716,19 @@ impl EditorElement { let editor = self.editor.read(cx); if editor.mouse_cursor_hidden { window.set_window_cursor_style(CursorStyle::None); + } else if let SelectionDragState::ReadyToDrag { + mouse_down_time, .. + } = &editor.selection_drag_state + { + let drag_and_drop_delay = Duration::from_millis( + EditorSettings::get_global(cx).drag_and_drop_selection.delay, + ); + if mouse_down_time.elapsed() >= drag_and_drop_delay { + window.set_cursor_style( + CursorStyle::DragCopy, + &layout.position_map.text_hitbox, + ); + } } else if matches!( editor.selection_drag_state, SelectionDragState::Dragging { .. } diff --git a/crates/migrator/src/migrations.rs b/crates/migrator/src/migrations.rs index 4e3839358b..9db597e964 100644 --- a/crates/migrator/src/migrations.rs +++ b/crates/migrator/src/migrations.rs @@ -93,3 +93,9 @@ pub(crate) mod m_2025_06_27 { pub(crate) use settings::SETTINGS_PATTERNS; } + +pub(crate) mod m_2025_07_08 { + mod settings; + + pub(crate) use settings::SETTINGS_PATTERNS; +} diff --git a/crates/migrator/src/migrations/m_2025_07_08/settings.rs b/crates/migrator/src/migrations/m_2025_07_08/settings.rs new file mode 100644 index 0000000000..c9656491ce --- /dev/null +++ b/crates/migrator/src/migrations/m_2025_07_08/settings.rs @@ -0,0 +1,37 @@ +use std::ops::Range; +use tree_sitter::{Query, QueryMatch}; + +use crate::MigrationPatterns; +use crate::patterns::SETTINGS_ROOT_KEY_VALUE_PATTERN; + +pub const SETTINGS_PATTERNS: MigrationPatterns = &[( + SETTINGS_ROOT_KEY_VALUE_PATTERN, + migrate_drag_and_drop_selection, +)]; + +fn migrate_drag_and_drop_selection( + contents: &str, + mat: &QueryMatch, + query: &Query, +) -> Option<(Range, String)> { + let name_ix = query.capture_index_for_name("name")?; + let name_range = mat.nodes_for_capture_index(name_ix).next()?.byte_range(); + let name = contents.get(name_range)?; + + if name != "drag_and_drop_selection" { + return None; + } + + let value_ix = query.capture_index_for_name("value")?; + let value_node = mat.nodes_for_capture_index(value_ix).next()?; + let value_range = value_node.byte_range(); + let value = contents.get(value_range.clone())?; + + match value { + "true" | "false" => { + let replacement = format!("{{\n \"enabled\": {}\n }}", value); + Some((value_range, replacement)) + } + _ => None, + } +} diff --git a/crates/migrator/src/migrator.rs b/crates/migrator/src/migrator.rs index be32b2734e..b425f7f1d5 100644 --- a/crates/migrator/src/migrator.rs +++ b/crates/migrator/src/migrator.rs @@ -160,6 +160,10 @@ pub fn migrate_settings(text: &str) -> Result> { migrations::m_2025_06_27::SETTINGS_PATTERNS, &SETTINGS_QUERY_2025_06_27, ), + ( + migrations::m_2025_07_08::SETTINGS_PATTERNS, + &SETTINGS_QUERY_2025_07_08, + ), ]; run_migrations(text, migrations) } @@ -270,6 +274,10 @@ define_query!( SETTINGS_QUERY_2025_06_27, migrations::m_2025_06_27::SETTINGS_PATTERNS ); +define_query!( + SETTINGS_QUERY_2025_07_08, + migrations::m_2025_07_08::SETTINGS_PATTERNS +); // custom query static EDIT_PREDICTION_SETTINGS_MIGRATION_QUERY: LazyLock = LazyLock::new(|| { diff --git a/docs/src/configuring-zed.md b/docs/src/configuring-zed.md index 8bba431554..eec9da60dd 100644 --- a/docs/src/configuring-zed.md +++ b/docs/src/configuring-zed.md @@ -1218,13 +1218,16 @@ or ### Drag And Drop Selection -- Description: Whether to allow drag and drop text selection in buffer. +- Description: Whether to allow drag and drop text selection in buffer. `delay` is the milliseconds that must elapse before drag and drop is allowed. Otherwise, a new text selection is created. - Setting: `drag_and_drop_selection` -- Default: `true` +- Default: -**Options** - -`boolean` values +```json +"drag_and_drop_selection": { + "enabled": true, + "delay": 300 +} +``` ## Editor Toolbar