diff --git a/assets/settings/default.json b/assets/settings/default.json index 46475ef0d3..f8d7406a9c 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -36,11 +36,13 @@ // Whether to show tabs and spaces in the editor. // This setting can take two values: // - // 1. Do not draw any tabs or spaces (default): + // 1. Draw tabs and spaces only for the selected text (default): + // "selection" + // 2. Do not draw any tabs or spaces: // "none" - // 2. Draw all invisible symbols: + // 3. Draw all invisible symbols: // "all" - "show_invisibles": "none", + "show_invisibles": "selection", // Whether the screen sharing icon is shown in the os status bar. "show_call_status_icon": true, // Whether to use language servers to provide code intelligence. diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 88436f43e8..428d697cb1 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -784,11 +784,15 @@ impl EditorElement { let mut cursors = SmallVec::<[Cursor; 32]>::new(); let corner_radius = 0.15 * layout.position_map.line_height; + let mut selection_ranges = SmallVec::<[Range; 32]>::new(); for (replica_id, selections) in &layout.selections { let selection_style = style.replica_selection_style(*replica_id); for selection in selections { + if !selection.range.is_empty() { + selection_ranges.push(selection.range.clone()); + } self.paint_highlighted_range( scene, selection.range.clone(), @@ -880,33 +884,42 @@ impl EditorElement { ); let settings = cx.global::(); - match settings + let regions_to_hit = match settings .editor_overrides .show_invisibles .or(settings.editor_defaults.show_invisibles) .unwrap_or_default() { - ShowInvisibles::None => {} - ShowInvisibles::All => { - for invisible in &line_with_invisibles.invisibles { - let (token_offset, invisible_symbol) = match invisible { - Invisible::Tab { line_start_offset } => { - (*line_start_offset, &layout.tab_invisible) - } - Invisible::Whitespace { line_offset } => { - (*line_offset, &layout.space_invisible) - } - }; + ShowInvisibles::None => continue, + ShowInvisibles::Selection => Some(&selection_ranges), + ShowInvisibles::All => None, + }; - let x_offset = line_with_invisibles.line.x_for_index(token_offset); - let invisible_offset = - (layout.position_map.em_width - invisible_symbol.width()).max(0.0) - / 2.0; - let origin = content_origin - + vec2f(-scroll_left + x_offset + invisible_offset, line_y); - invisible_symbol.paint(scene, origin, visible_bounds, line_height, cx); + for invisible in &line_with_invisibles.invisibles { + let (&token_offset, invisible_symbol) = match invisible { + Invisible::Tab { line_start_offset } => { + (line_start_offset, &layout.tab_invisible) + } + Invisible::Whitespace { line_offset } => { + (line_offset, &layout.space_invisible) + } + }; + + let x_offset = line_with_invisibles.line.x_for_index(token_offset); + let invisible_offset = + (layout.position_map.em_width - invisible_symbol.width()).max(0.0) / 2.0; + let origin = + content_origin + vec2f(-scroll_left + x_offset + invisible_offset, line_y); + + if let Some(regions_to_hit) = regions_to_hit { + let invisible_point = DisplayPoint::new(row, token_offset as u32); + if !regions_to_hit.iter().any(|region| { + region.start <= invisible_point && invisible_point < region.end + }) { + continue; } } + invisible_symbol.paint(scene, origin, visible_bounds, line_height, cx); } } } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index eee57eaec6..2e5fd1353a 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -451,6 +451,7 @@ pub struct FeaturesContent { #[serde(rename_all = "snake_case")] pub enum ShowInvisibles { #[default] + Selection, None, All, }