mirror of
https://github.com/wez/wezterm.git
synced 2024-11-23 15:04:36 +03:00
add rectangular selection
Alt-dragging will use rectangular selection in the default mouse assignments. refs: https://github.com/wez/wezterm/issues/1361
This commit is contained in:
parent
1b00bbaf2f
commit
ef4a95211e
@ -95,6 +95,7 @@ pub enum SelectionMode {
|
||||
Word,
|
||||
Line,
|
||||
SemanticZone,
|
||||
Block,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||
|
@ -25,6 +25,7 @@ As features stabilize some brief notes about them will accumulate here.
|
||||
* `wezterm cli list --format json` and `wezterm cli list-clients --format json` allow retrieving data in json format. Thanks to [@ratmice](https://github.com/ratmice)! [#1911](https://github.com/wez/wezterm/pull/1911)
|
||||
* macOS: you may now drag and drop files from other programs and have their paths paste into the terminal. The new [quote_dropped_files](config/lua/config/quote_dropped_files.md) option controls how the file names are quoted. Thanks to [@junnplus](https://github.com/junnplus)! [#1868](https://github.com/wez/wezterm/pull/1868)
|
||||
* The mouse scroll wheel now cycles between tabs when hovering over the tab tab. Thanks to [@junnplus](https://github.com/junnplus)! [#1726](https://github.com/wez/wezterm/issues/1726)
|
||||
* Holding down `ALT` while dragging the left button will select a rectangular block. [ExtendSelectionToMouseCursor](config/lua/keyassignment/ExtendSelectionToMouseCursor.md) now accepts `"Block"` as a selection mode. [#1361](https://github.com/wez/wezterm/issues/1361)
|
||||
|
||||
#### Updated
|
||||
* Bundled harfbuzz to 4.2.1
|
||||
|
@ -23,4 +23,6 @@ when unspecified, wezterm will use a default mode which at the time
|
||||
of writing is `Cell`, but in a future release may be context sensitive
|
||||
based on recent actions.
|
||||
|
||||
*Since: nightly builds only*
|
||||
|
||||
The mode argument can also be `"Block"` to enable a rectangular block selection.
|
||||
|
@ -32,11 +32,14 @@ that order.
|
||||
| Double Left Down | `NONE` | `SelectTextAtMouseCursor="Word"` |
|
||||
| Single Left Down | `NONE` | `SelectTextAtMouseCursor="Cell"` |
|
||||
| Single Left Down | `SHIFT` | `ExtendSelectionToMouseCursor={}` |
|
||||
| Single Left Down | `ALT` | `ExtendSelectionToMouseCursor="Block"` (*since: nightly builds only*) |
|
||||
| Single Left Up | `SHIFT` | `CompleteSelectionOrOpenLinkAtMouseCursor="PrimarySelection"` |
|
||||
| Single Left Up | `NONE` | `CompleteSelectionOrOpenLinkAtMouseCursor="PrimarySelection"` |
|
||||
| Single Left Up | `ALT` | `CompleteSelection="PrimarySelection"` (*since: nightly builds only*) |
|
||||
| Double Left Up | `NONE` | `CompleteSelection="PrimarySelection"` |
|
||||
| Triple Left Up | `NONE` | `CompleteSelection="PrimarySelection"` |
|
||||
| Single Left Drag | `NONE` | `ExtendSelectionToMouseCursor="Cell"` |
|
||||
| Single Left Drag | `ALT` | `ExtendSelectionToMouseCursor="Block"` (*since: nightly builds only*) |
|
||||
| Double Left Drag | `NONE` | `ExtendSelectionToMouseCursor="Word"` |
|
||||
| Triple Left Drag | `NONE` | `ExtendSelectionToMouseCursor="Line"` |
|
||||
| Single Middle Down | `NONE` | `PasteFrom="PrimarySelection"` |
|
||||
|
@ -75,6 +75,14 @@ impl InputMap {
|
||||
},
|
||||
SelectTextAtMouseCursor(SelectionMode::Cell)
|
||||
],
|
||||
[
|
||||
Modifiers::ALT,
|
||||
MouseEventTrigger::Down {
|
||||
streak: 1,
|
||||
button: MouseButton::Left
|
||||
},
|
||||
SelectTextAtMouseCursor(SelectionMode::Block)
|
||||
],
|
||||
[
|
||||
Modifiers::SHIFT,
|
||||
MouseEventTrigger::Down {
|
||||
@ -103,6 +111,14 @@ impl InputMap {
|
||||
ClipboardCopyDestination::PrimarySelection
|
||||
)
|
||||
],
|
||||
[
|
||||
Modifiers::ALT,
|
||||
MouseEventTrigger::Up {
|
||||
streak: 1,
|
||||
button: MouseButton::Left
|
||||
},
|
||||
CompleteSelection(ClipboardCopyDestination::PrimarySelection)
|
||||
],
|
||||
[
|
||||
Modifiers::NONE,
|
||||
MouseEventTrigger::Up {
|
||||
@ -127,6 +143,14 @@ impl InputMap {
|
||||
},
|
||||
ExtendSelectionToMouseCursor(Some(SelectionMode::Cell))
|
||||
],
|
||||
[
|
||||
Modifiers::ALT,
|
||||
MouseEventTrigger::Drag {
|
||||
streak: 1,
|
||||
button: MouseButton::Left
|
||||
},
|
||||
ExtendSelectionToMouseCursor(Some(SelectionMode::Block))
|
||||
],
|
||||
[
|
||||
Modifiers::NONE,
|
||||
MouseEventTrigger::Drag {
|
||||
|
@ -17,6 +17,8 @@ pub struct Selection {
|
||||
pub range: Option<SelectionRange>,
|
||||
/// When the selection was made wrt. the pane content
|
||||
pub seqno: SequenceNo,
|
||||
/// Whether the selection is rectangular
|
||||
pub rectangular: bool,
|
||||
}
|
||||
|
||||
pub use config::keyassignment::SelectionMode;
|
||||
@ -215,9 +217,9 @@ impl SelectionRange {
|
||||
/// Since this struct has no knowledge of line length, it cannot be
|
||||
/// more precise than that.
|
||||
/// Must be called on a normalized range!
|
||||
pub fn cols_for_row(&self, row: StableRowIndex) -> Range<usize> {
|
||||
pub fn cols_for_row(&self, row: StableRowIndex, rectangular: bool) -> Range<usize> {
|
||||
let norm = self.normalize();
|
||||
if row < norm.start.y || row > norm.end.y {
|
||||
let range = if row < norm.start.y || row > norm.end.y {
|
||||
0..0
|
||||
} else if norm.start.y == norm.end.y {
|
||||
// A single line selection
|
||||
@ -235,6 +237,12 @@ impl SelectionRange {
|
||||
} else {
|
||||
// some "middle" line of multi-line
|
||||
0..usize::max_value()
|
||||
};
|
||||
|
||||
if rectangular {
|
||||
range.start.max(norm.start.x)..range.end.min(norm.end.x.saturating_add(1))
|
||||
} else {
|
||||
range
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1349,7 +1349,10 @@ impl super::TermWindow {
|
||||
)?;
|
||||
}
|
||||
|
||||
let selrange = self.selection(pos.pane.pane_id()).range.clone();
|
||||
let (selrange, rectangular) = {
|
||||
let sel = self.selection(pos.pane.pane_id());
|
||||
(sel.range.clone(), sel.rectangular)
|
||||
};
|
||||
|
||||
let start = Instant::now();
|
||||
let selection_fg = palette.selection_fg.to_linear();
|
||||
@ -1362,7 +1365,7 @@ impl super::TermWindow {
|
||||
for (line_idx, line) in lines.iter().enumerate() {
|
||||
let stable_row = stable_top + line_idx as StableRowIndex;
|
||||
|
||||
let selrange = selrange.map_or(0..0, |sel| sel.cols_for_row(stable_row));
|
||||
let selrange = selrange.map_or(0..0, |sel| sel.cols_for_row(stable_row, rectangular));
|
||||
// Constrain to the pane width!
|
||||
let selrange = selrange.start..selrange.end.min(dims.cols);
|
||||
|
||||
|
@ -12,6 +12,7 @@ impl super::TermWindow {
|
||||
|
||||
pub fn selection_text(&self, pane: &Rc<dyn Pane>) -> String {
|
||||
let mut s = String::new();
|
||||
let rectangular = self.selection(pane.pane_id()).rectangular;
|
||||
if let Some(sel) = self
|
||||
.selection(pane.pane_id())
|
||||
.range
|
||||
@ -31,7 +32,7 @@ impl super::TermWindow {
|
||||
let this_row = line.first_row + idx as StableRowIndex;
|
||||
if this_row >= first_row && this_row < last_row {
|
||||
let last_phys_idx = phys.cells().len().saturating_sub(1);
|
||||
let cols = sel.cols_for_row(this_row);
|
||||
let cols = sel.cols_for_row(this_row, rectangular);
|
||||
let last_col_idx = cols.end.saturating_sub(1).min(last_phys_idx);
|
||||
let col_span = phys.columns_as_str(cols);
|
||||
// Only trim trailing whitespace if we are the last line
|
||||
@ -76,7 +77,7 @@ impl super::TermWindow {
|
||||
};
|
||||
let x = position.column;
|
||||
match mode {
|
||||
SelectionMode::Cell => {
|
||||
SelectionMode::Cell | SelectionMode::Block => {
|
||||
// Origin is the cell in which the selection action started. E.g. the cell
|
||||
// that had the mouse over it when the left mouse button was pressed
|
||||
let origin = self
|
||||
@ -84,6 +85,7 @@ impl super::TermWindow {
|
||||
.origin
|
||||
.unwrap_or(SelectionCoordinate { x, y });
|
||||
self.selection(pane.pane_id()).origin = Some(origin);
|
||||
self.selection(pane.pane_id()).rectangular = mode == SelectionMode::Block;
|
||||
|
||||
// Compute the start and end horizontall cell of the selection.
|
||||
// The selection extent depends on the mouse cursor position in relation
|
||||
@ -128,6 +130,7 @@ impl super::TermWindow {
|
||||
|
||||
let selection_range = start_word.extend_with(end_word);
|
||||
self.selection(pane.pane_id()).range = Some(selection_range);
|
||||
self.selection(pane.pane_id()).rectangular = false;
|
||||
}
|
||||
SelectionMode::Line => {
|
||||
let end_line = SelectionRange::line_around(SelectionCoordinate { x, y }, &**pane);
|
||||
@ -141,6 +144,7 @@ impl super::TermWindow {
|
||||
|
||||
let selection_range = start_line.extend_with(end_line);
|
||||
self.selection(pane.pane_id()).range = Some(selection_range);
|
||||
self.selection(pane.pane_id()).rectangular = false;
|
||||
}
|
||||
SelectionMode::SemanticZone => {
|
||||
let end_word = SelectionRange::zone_around(SelectionCoordinate { x, y }, &**pane);
|
||||
@ -154,6 +158,7 @@ impl super::TermWindow {
|
||||
|
||||
let selection_range = start_word.extend_with(end_word);
|
||||
self.selection(pane.pane_id()).range = Some(selection_range);
|
||||
self.selection(pane.pane_id()).rectangular = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,6 +189,7 @@ impl super::TermWindow {
|
||||
|
||||
self.selection(pane.pane_id()).origin = Some(start);
|
||||
self.selection(pane.pane_id()).range = Some(selection_range);
|
||||
self.selection(pane.pane_id()).rectangular = false;
|
||||
}
|
||||
SelectionMode::Word => {
|
||||
let selection_range =
|
||||
@ -191,6 +197,7 @@ impl super::TermWindow {
|
||||
|
||||
self.selection(pane.pane_id()).origin = Some(selection_range.start);
|
||||
self.selection(pane.pane_id()).range = Some(selection_range);
|
||||
self.selection(pane.pane_id()).rectangular = false;
|
||||
}
|
||||
SelectionMode::SemanticZone => {
|
||||
let selection_range =
|
||||
@ -198,10 +205,12 @@ impl super::TermWindow {
|
||||
|
||||
self.selection(pane.pane_id()).origin = Some(selection_range.start);
|
||||
self.selection(pane.pane_id()).range = Some(selection_range);
|
||||
self.selection(pane.pane_id()).rectangular = false;
|
||||
}
|
||||
SelectionMode::Cell => {
|
||||
SelectionMode::Cell | SelectionMode::Block => {
|
||||
self.selection(pane.pane_id())
|
||||
.begin(SelectionCoordinate { x, y });
|
||||
self.selection(pane.pane_id()).rectangular = mode == SelectionMode::Block;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user