@@ -447,6 +447,7 @@ pub enum SelectPhase {
BeginColumnar {
position: DisplayPoint,
reset: bool,
+ mode: ColumnarMode,
goal_column: u32,
},
Extend {
@@ -461,6 +462,12 @@ pub enum SelectPhase {
End,
}
+#[derive(Clone, Debug, PartialEq)]
+pub enum ColumnarMode {
+ FromMouse,
+ FromSelection,
+}
+
#[derive(Clone, Debug)]
pub enum SelectMode {
Character,
@@ -922,6 +929,16 @@ enum SelectionDragState {
},
}
+enum ColumnarSelectionState {
+ FromMouse {
+ selection_tail: Anchor,
+ display_point: Option<DisplayPoint>,
+ },
+ FromSelection {
+ selection_tail: Anchor,
+ },
+}
+
/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
/// a breakpoint on them.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -949,8 +966,7 @@ pub struct Editor {
/// When inline assist editors are linked, they all render cursors because
/// typing enters text into each of them, even the ones that aren't focused.
pub(crate) show_cursor_when_unfocused: bool,
- columnar_selection_tail: Option<Anchor>,
- columnar_display_point: Option<DisplayPoint>,
+ columnar_selection_state: Option<ColumnarSelectionState>,
add_selections_state: Option<AddSelectionsState>,
select_next_state: Option<SelectNextState>,
select_prev_state: Option<SelectNextState>,
@@ -1893,8 +1909,7 @@ impl Editor {
display_map: display_map.clone(),
selections,
scroll_manager: ScrollManager::new(cx),
- columnar_selection_tail: None,
- columnar_display_point: None,
+ columnar_selection_state: None,
add_selections_state: None,
select_next_state: None,
select_prev_state: None,
@@ -3216,7 +3231,8 @@ impl Editor {
position,
goal_column,
reset,
- } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
+ mode,
+ } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
SelectPhase::Extend {
position,
click_count,
@@ -3373,6 +3389,7 @@ impl Editor {
position: DisplayPoint,
goal_column: u32,
reset: bool,
+ mode: ColumnarMode,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -3395,26 +3412,30 @@ impl Editor {
SelectMode::Character,
);
});
- if position.column() != goal_column {
- self.columnar_display_point = Some(DisplayPoint::new(position.row(), goal_column));
- } else {
- self.columnar_display_point = None;
- }
- }
+ };
let tail = self.selections.newest::<Point>(cx).tail();
- self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
+ let selection_anchor = display_map.buffer_snapshot.anchor_before(tail);
+ self.columnar_selection_state = match mode {
+ ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
+ selection_tail: selection_anchor,
+ display_point: if reset {
+ if position.column() != goal_column {
+ Some(DisplayPoint::new(position.row(), goal_column))
+ } else {
+ None
+ }
+ } else {
+ None
+ },
+ }),
+ ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
+ selection_tail: selection_anchor,
+ }),
+ };
if !reset {
- self.columnar_display_point = None;
- self.select_columns(
- tail.to_display_point(&display_map),
- position,
- goal_column,
- &display_map,
- window,
- cx,
- );
+ self.select_columns(position, goal_column, &display_map, window, cx);
}
}
@@ -3428,11 +3449,8 @@ impl Editor {
) {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
- if let Some(tail) = self.columnar_selection_tail.as_ref() {
- let tail = self
- .columnar_display_point
- .unwrap_or_else(|| tail.to_display_point(&display_map));
- self.select_columns(tail, position, goal_column, &display_map, window, cx);
+ if self.columnar_selection_state.is_some() {
+ self.select_columns(position, goal_column, &display_map, window, cx);
} else if let Some(mut pending) = self.selections.pending_anchor() {
let buffer = self.buffer.read(cx).snapshot(cx);
let head;
@@ -3519,7 +3537,7 @@ impl Editor {
}
fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
- self.columnar_selection_tail.take();
+ self.columnar_selection_state.take();
if self.selections.pending_anchor().is_some() {
let selections = self.selections.all::<usize>(cx);
self.change_selections(None, window, cx, |s| {
@@ -3531,13 +3549,26 @@ impl Editor {
fn select_columns(
&mut self,
- tail: DisplayPoint,
head: DisplayPoint,
goal_column: u32,
display_map: &DisplaySnapshot,
window: &mut Window,
cx: &mut Context<Self>,
) {
+ let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
+ return;
+ };
+
+ let tail = match columnar_state {
+ ColumnarSelectionState::FromMouse {
+ selection_tail,
+ display_point,
+ } => display_point.unwrap_or_else(|| selection_tail.to_display_point(&display_map)),
+ ColumnarSelectionState::FromSelection { selection_tail } => {
+ selection_tail.to_display_point(&display_map)
+ }
+ };
+
let start_row = cmp::min(tail.row(), head.row());
let end_row = cmp::max(tail.row(), head.row());
let start_column = cmp::min(tail.column(), goal_column);
@@ -3547,7 +3578,10 @@ impl Editor {
let selection_ranges = (start_row.0..=end_row.0)
.map(DisplayRow)
.filter_map(|row| {
- if !display_map.is_block_line(row) {
+ if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
+ || start_column <= display_map.line_len(row))
+ && !display_map.is_block_line(row)
+ {
let start = display_map
.clip_point(DisplayPoint::new(row, start_column), Bias::Left)
.to_point(display_map);
@@ -3565,15 +3599,19 @@ impl Editor {
})
.collect::<Vec<_>>();
- let mut non_empty_ranges = selection_ranges
- .iter()
- .filter(|selection_range| selection_range.start != selection_range.end)
- .peekable();
-
- let ranges = if non_empty_ranges.peek().is_some() {
- non_empty_ranges.cloned().collect()
- } else {
- selection_ranges
+ let ranges = match columnar_state {
+ ColumnarSelectionState::FromMouse { .. } => {
+ let mut non_empty_ranges = selection_ranges
+ .iter()
+ .filter(|selection_range| selection_range.start != selection_range.end)
+ .peekable();
+ if non_empty_ranges.peek().is_some() {
+ non_empty_ranges.cloned().collect()
+ } else {
+ selection_ranges
+ }
+ }
+ _ => selection_ranges,
};
self.change_selections(None, window, cx, |s| {
@@ -3596,11 +3634,11 @@ impl Editor {
};
pending_nonempty_selection
- || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
+ || (self.columnar_selection_state.is_some() && self.selections.disjoint.len() > 1)
}
pub fn has_pending_selection(&self) -> bool {
- self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
+ self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
}
pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
@@ -7188,13 +7226,9 @@ impl Editor {
)
}
- fn multi_cursor_modifier(
- cursor_event: bool,
- modifiers: &Modifiers,
- cx: &mut Context<Self>,
- ) -> bool {
+ fn multi_cursor_modifier(invert: bool, modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
- if cursor_event {
+ if invert {
match multi_cursor_setting {
MultiCursorModifier::Alt => modifiers.alt,
MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
@@ -7207,8 +7241,21 @@ impl Editor {
}
}
- fn columnar_selection_modifiers(multi_cursor_modifier: bool, modifiers: &Modifiers) -> bool {
- modifiers.shift && multi_cursor_modifier && modifiers.number_of_modifiers() == 2
+ fn columnar_selection_mode(
+ modifiers: &Modifiers,
+ cx: &mut Context<Self>,
+ ) -> Option<ColumnarMode> {
+ if modifiers.shift && modifiers.number_of_modifiers() == 2 {
+ if Self::multi_cursor_modifier(false, modifiers, cx) {
+ Some(ColumnarMode::FromMouse)
+ } else if Self::multi_cursor_modifier(true, modifiers, cx) {
+ Some(ColumnarMode::FromSelection)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
}
fn update_selection_mode(
@@ -7218,10 +7265,10 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
- let multi_cursor_modifier = Self::multi_cursor_modifier(true, modifiers, cx);
- if !Self::columnar_selection_modifiers(multi_cursor_modifier, modifiers)
- || self.selections.pending.is_none()
- {
+ let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
+ return;
+ };
+ if self.selections.pending.is_none() {
return;
}
@@ -7233,6 +7280,7 @@ impl Editor {
SelectPhase::BeginColumnar {
position,
reset: false,
+ mode,
goal_column: point_for_position.exact_unclipped.column(),
},
window,
@@ -1,13 +1,13 @@
use crate::{
ActiveDiagnostic, BlockId, CURSORS_VISIBLE_FOR, ChunkRendererContext, ChunkReplacement,
- CodeActionSource, ConflictsOurs, ConflictsOursMarker, ConflictsOuter, ConflictsTheirs,
- ConflictsTheirsMarker, ContextMenuPlacement, CursorShape, CustomBlockId, DisplayDiffHunk,
- DisplayPoint, DisplayRow, DocumentHighlightRead, DocumentHighlightWrite, EditDisplayMode,
- Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, FILE_HEADER_HEIGHT,
- FocusedBlock, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor,
- InlayHintRefreshReason, InlineCompletion, JumpData, LineDown, LineHighlight, LineUp,
- MAX_LINE_LEN, MINIMAP_FONT_SIZE, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, OpenExcerpts, PageDown,
- PageUp, PhantomBreakpointIndicator, Point, RowExt, RowRangeExt, SelectPhase,
+ CodeActionSource, ColumnarMode, ConflictsOurs, ConflictsOursMarker, ConflictsOuter,
+ ConflictsTheirs, ConflictsTheirsMarker, ContextMenuPlacement, CursorShape, CustomBlockId,
+ DisplayDiffHunk, DisplayPoint, DisplayRow, DocumentHighlightRead, DocumentHighlightWrite,
+ EditDisplayMode, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle,
+ FILE_HEADER_HEIGHT, FocusedBlock, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput,
+ HoveredCursor, InlayHintRefreshReason, InlineCompletion, JumpData, LineDown, LineHighlight,
+ LineUp, MAX_LINE_LEN, MINIMAP_FONT_SIZE, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, OpenExcerpts,
+ PageDown, PageUp, PhantomBreakpointIndicator, Point, RowExt, RowRangeExt, SelectPhase,
SelectedTextHighlight, Selection, SelectionDragState, SoftWrap, StickyHeaderExcerpt, ToPoint,
ToggleFold,
code_context_menus::{CodeActionsMenu, MENU_ASIDE_MAX_WIDTH, MENU_ASIDE_MIN_WIDTH, MENU_GAP},
@@ -700,12 +700,15 @@ impl EditorElement {
}
let position = point_for_position.previous_valid;
- let multi_cursor_modifier = Editor::multi_cursor_modifier(true, &modifiers, cx);
- if Editor::columnar_selection_modifiers(multi_cursor_modifier, &modifiers) {
+ if let Some(mode) = Editor::columnar_selection_mode(&modifiers, cx) {
editor.select(
SelectPhase::BeginColumnar {
position,
- reset: true,
+ reset: match mode {
+ ColumnarMode::FromMouse => true,
+ ColumnarMode::FromSelection => false,
+ },
+ mode: mode,
goal_column: point_for_position.exact_unclipped.column(),
},
window,
@@ -725,7 +728,7 @@ impl EditorElement {
editor.select(
SelectPhase::Begin {
position,
- add: multi_cursor_modifier,
+ add: Editor::multi_cursor_modifier(true, &modifiers, cx),
click_count,
},
window,
@@ -822,6 +825,7 @@ impl EditorElement {
SelectPhase::BeginColumnar {
position,
reset: true,
+ mode: ColumnarMode::FromMouse,
goal_column: point_for_position.exact_unclipped.column(),
},
window,