1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 13:21:38 +03:00

wezterm: add SelectionMode::SemanticZone

This makes it possible to configure wezterm to eg: triple click
on command input (or output) to select the entire input or output
without messing around trying to find the bounds.

The docs have an example of how to configure this; it requires
setting up shell integration to define the appropriate semantic
zones.
This commit is contained in:
Wez Furlong 2020-11-15 22:23:18 -08:00
parent 720a6fd9b6
commit e8ad765ff5
6 changed files with 95 additions and 2 deletions

View File

@ -12,6 +12,7 @@ pub enum SelectionMode {
Cell,
Word,
Line,
SemanticZone,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]

View File

@ -16,6 +16,8 @@ brief notes about them may accumulate here.
* Add [`ScrollToPrompt`](config/lua/keyassignment/ScrollToPrompt.md) key assignment that scrolls the viewport to the prior/next shell prompt emitted using OSC 133 Semantic Prompt escapes. This assignment is not bound by default.
* Fixed an issue where `SpawnWindow` didn't use the current working directory from the current pane to spawn the new window
* Added `wezterm start --class CLASSNAME` option to specify the window class name under X11 and Windows, or the `app_id` under Wayland. See `wezterm start --help` for more information.
* Added shell integration for setting OSC 7 (working directory) and OSC 133 (semantic zones) for Zsh and Bash. [See Shell Integration docs](shell-integration.md).
* Added `SemanticZone` as a possible parameter for [SelectTextAtMouseCursor](config/lua/keyassignment/SelectTextAtMouseCursor.md), making it possible to conveniently select complete input or output regions.
### 20201101-103216-403d002d

View File

@ -4,4 +4,26 @@ Initiates selection of text at the current mouse cursor position.
The mode argument can be one of `Cell`, `Word` or `Line` to control
the scope of the selection.
*Since: nightly builds only*
The mode argument can be `SemanticZone` which causes the selection
to take the surrounding semantic zone.
In this example, the triple-left-click mouse action is set to
automatically select the entire command output when clicking
on any character withing that region:
```lua
return {
mouse_bindings = {
{ event={Down={streak=3, button="Left"}},
action={SelectTextAtMouseCursor="SemanticZone"},
mods="NONE"
},
},
}
```
[See Shell Integration docs](../../../shell-integration.md) for more details on
how to set up your shell to define semantic zones.

View File

@ -7,7 +7,8 @@ wezterm supports integrating with the shell through the following means:
These sequences enable some improved user experiences, such as being able
to spawn new panes, tabs and windows with the same current working directory
as the current pane, or [jumping through the scrollback to the start of an earlier command](config/lua/keyassignment/ScrollToPrompt.md).
as the current pane, [jumping through the scrollback to the start of an earlier command](config/lua/keyassignment/ScrollToPrompt.md),
or [conveniently selecting the complete output from a command](config/lua/keyassignment/SelectTextAtMouseCursor.md).
In order for these features to be enabled, you will need to configure your
shell program to emit the escape sequences at the appropriate place.

View File

@ -2,9 +2,10 @@
// and inclusive range
#![cfg_attr(feature = "cargo-clippy", allow(clippy::range_plus_one))]
use mux::renderable::Renderable;
use std::cmp::Ordering;
use std::ops::Range;
use termwiz::surface::line::DoubleClickRange;
use wezterm_term::StableRowIndex;
use wezterm_term::{SemanticZone, StableRowIndex};
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct Selection {
@ -80,6 +81,52 @@ impl SelectionRange {
}
}
pub fn zone_around(start: SelectionCoordinate, pane: &dyn mux::pane::Pane) -> Self {
let zones = match pane.get_semantic_zones() {
Ok(z) => z,
Err(_) => return Self { start, end: start },
};
fn find_zone(start: &SelectionCoordinate, zone: &SemanticZone) -> Ordering {
match zone.start_y.cmp(&start.y) {
Ordering::Greater => return Ordering::Greater,
// If the zone starts on the same line then check that the
// x position is within bounds
Ordering::Equal => match zone.start_x.cmp(&start.x) {
Ordering::Greater => return Ordering::Greater,
Ordering::Equal | Ordering::Less => {}
},
Ordering::Less => {}
}
match zone.end_y.cmp(&start.y) {
Ordering::Less => Ordering::Less,
// If the zone ends on the same line then check that the
// x position is within bounds
Ordering::Equal => match zone.end_x.cmp(&start.x) {
Ordering::Less => Ordering::Less,
Ordering::Equal | Ordering::Greater => Ordering::Equal,
},
Ordering::Greater => Ordering::Equal,
}
}
if let Ok(idx) = zones.binary_search_by(|zone| find_zone(&start, zone)) {
let zone = &zones[idx];
Self {
start: SelectionCoordinate {
x: zone.start_x,
y: zone.start_y,
},
end: SelectionCoordinate {
x: zone.end_x,
y: zone.end_y,
},
}
} else {
Self { start, end: start }
}
}
/// Computes the selection range for the word around the specified coords
pub fn word_around(start: SelectionCoordinate, renderer: &mut dyn Renderable) -> Self {
let (first, lines) = renderer.get_lines(start.y..start.y + 1);

View File

@ -3302,6 +3302,19 @@ impl TermWindow {
let selection_range = start_line.extend_with(end_line);
self.selection(pane.pane_id()).range = Some(selection_range);
}
SelectionMode::SemanticZone => {
let end_word = SelectionRange::zone_around(SelectionCoordinate { x, y }, &**pane);
let start_coord = self
.selection(pane.pane_id())
.start
.clone()
.unwrap_or(end_word.start);
let start_word = SelectionRange::zone_around(start_coord, &**pane);
let selection_range = start_word.extend_with(end_word);
self.selection(pane.pane_id()).range = Some(selection_range);
}
}
// When the mouse gets close enough to the top or bottom then scroll
@ -3354,6 +3367,13 @@ impl TermWindow {
self.selection(pane.pane_id()).start = Some(selection_range.start);
self.selection(pane.pane_id()).range = Some(selection_range);
}
SelectionMode::SemanticZone => {
let selection_range =
SelectionRange::zone_around(SelectionCoordinate { x, y }, &**pane);
self.selection(pane.pane_id()).start = Some(selection_range.start);
self.selection(pane.pane_id()).range = Some(selection_range);
}
SelectionMode::Cell => {
self.selection(pane.pane_id())
.begin(SelectionCoordinate { x, y });