mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 21:32:13 +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:
parent
720a6fd9b6
commit
e8ad765ff5
@ -12,6 +12,7 @@ pub enum SelectionMode {
|
|||||||
Cell,
|
Cell,
|
||||||
Word,
|
Word,
|
||||||
Line,
|
Line,
|
||||||
|
SemanticZone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
|
@ -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.
|
* 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
|
* 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 `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
|
### 20201101-103216-403d002d
|
||||||
|
|
||||||
|
@ -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 mode argument can be one of `Cell`, `Word` or `Line` to control
|
||||||
the scope of the selection.
|
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.
|
||||||
|
|
||||||
|
@ -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
|
These sequences enable some improved user experiences, such as being able
|
||||||
to spawn new panes, tabs and windows with the same current working directory
|
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
|
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.
|
shell program to emit the escape sequences at the appropriate place.
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
// and inclusive range
|
// and inclusive range
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::range_plus_one))]
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::range_plus_one))]
|
||||||
use mux::renderable::Renderable;
|
use mux::renderable::Renderable;
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use termwiz::surface::line::DoubleClickRange;
|
use termwiz::surface::line::DoubleClickRange;
|
||||||
use wezterm_term::StableRowIndex;
|
use wezterm_term::{SemanticZone, StableRowIndex};
|
||||||
|
|
||||||
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
|
||||||
pub struct Selection {
|
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
|
/// Computes the selection range for the word around the specified coords
|
||||||
pub fn word_around(start: SelectionCoordinate, renderer: &mut dyn Renderable) -> Self {
|
pub fn word_around(start: SelectionCoordinate, renderer: &mut dyn Renderable) -> Self {
|
||||||
let (first, lines) = renderer.get_lines(start.y..start.y + 1);
|
let (first, lines) = renderer.get_lines(start.y..start.y + 1);
|
||||||
|
@ -3302,6 +3302,19 @@ impl TermWindow {
|
|||||||
let selection_range = start_line.extend_with(end_line);
|
let selection_range = start_line.extend_with(end_line);
|
||||||
self.selection(pane.pane_id()).range = Some(selection_range);
|
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
|
// 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()).start = Some(selection_range.start);
|
||||||
self.selection(pane.pane_id()).range = Some(selection_range);
|
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 => {
|
SelectionMode::Cell => {
|
||||||
self.selection(pane.pane_id())
|
self.selection(pane.pane_id())
|
||||||
.begin(SelectionCoordinate { x, y });
|
.begin(SelectionCoordinate { x, y });
|
||||||
|
Loading…
Reference in New Issue
Block a user