1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-22 22:42:48 +03:00

lua: add some pane methods for working with zones

refs: https://github.com/wez/wezterm/issues/2968
This commit is contained in:
Wez Furlong 2023-01-18 19:34:49 -07:00
parent a2e027dff4
commit 15cd1afbdc
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387
8 changed files with 250 additions and 21 deletions

View File

@ -29,6 +29,10 @@ As features stabilize some brief notes about them will accumulate here.
option to control whether the mouse cursor is hidden when typing. Thanks to
[@ProspectPyxis](https://github.com/ProspectPyxis)!
[#2946](https://github.com/wez/wezterm/pull/2946)
* [pane:get_text_from_region()](config/lua/pane/get_text_from_region.md),
[pane:get_text_from_semantic_zone()](config/lua/pane/get_text_from_semantic_zone.md),
[pane:get_semantic_zones()](config/lua/pane/get_semantic_zones.md),
[pane:get_semantic_zone_at()](config/lua/pane/get_semantic_zone_at.md)
* Color schemes: [Apple Classic](colorschemes/a/index.md#apple-classic),
[\_bash (Gogh)](colorschemes/b/index.md#bash-gogh),
[Breath (Gogh)](colorschemes/b/index.md#breath-gogh),

View File

@ -0,0 +1,30 @@
# `pane:get_semantic_zone_at(x, y)`
*Since: nightly builds only*
Resolves the semantic zone that encapsulates the supplied *x* and *y* coordinates.
*x* is the cell column index, where 0 is the left-most column.
*y* is the stable row index.
Use [pane:get_dimensions()](get_dimensions.md) to
retrieve the currently valid stable index values for the top of scrollback and
top of viewport.
```lua
-- If you have shell integration configured, returns the zone around
-- the current cursor position
function get_zone_around_cursor(pane)
local cursor = pane:get_cursor_position()
-- using x-1 here because the cursor may be one cell outside the zone
local zone = pane:get_semantic_zone_at(cursor.x - 1, cursor.y)
if zone then
return pane:get_text_from_semantic_zone(zone)
end
return nil
end
```
See [Shell Integration](../../../shell-integration.md) for more information
about semantic zones.

View File

@ -0,0 +1,17 @@
# `pane:get_semantic_zones([zone_type])`
*Since: nightly builds only*
When *zone_type* is omitted, returns the list of all semantic zones defined in the pane.
When *zone_type* is supplied, returns the list of all semantic zones of the matching type.
Value values for *zone_type* are:
* `"Prompt"`
* `"Input"`
* `"Output"`
See [Shell Integration](../../../shell-integration.md) for more information
about semantic zones.

View File

@ -0,0 +1,16 @@
# `pane:get_text_from_region(start_x, start_y, end_x, end_y)`
*Since: nightly builds only*
Returns the text from the specified region.
* `start_x` and `end_x` are the starting and ending cell column, where 0 is the
left-most cell
* `start_y` and `end_y` are the starting and ending row, expressed as a stable
row index. Use [pane:get_dimensions()](get_dimensions.md) to retrieve the
currently valid stable index values for the top of scrollback and top of
viewport.
The text within the region is unwrapped to its logical line representation,
rather than the wrapped-to-physical-display-width.

View File

@ -0,0 +1,12 @@
# `pane:get_text_from_semantic_zone(zone)`
*Since: nightly builds only*
This is a convenience method that calls [pane:get_text_from_region()](get_text_from_region.md) on the supplied *zone* parameter.
Use [pane:get_semantic_zone_at()](get_semantic_zone_at.md) or
[pane:get_semantic_zones()](get_semantic_zones.md) to obtain a *zone*.
See [Shell Integration](../../../shell-integration.md) for more information
about semantic zones.

View File

@ -1,6 +1,10 @@
use super::*;
use luahelper::dynamic_to_lua_value;
use luahelper::{dynamic_to_lua_value, from_lua, to_lua};
use mlua::Value;
use std::cmp::Ordering;
use std::sync::Arc;
use termwiz::cell::SemanticType;
use wezterm_term::{SemanticZone, StableRowIndex};
#[derive(Clone, Copy, Debug)]
pub struct MuxPane(pub PaneId);
@ -10,6 +14,70 @@ impl MuxPane {
mux.get_pane(self.0)
.ok_or_else(|| mlua::Error::external(format!("pane id {} not found in mux", self.0)))
}
fn get_text_from_semantic_zone(&self, zone: SemanticZone) -> mlua::Result<String> {
let mux = get_mux()?;
let pane = self.resolve(&mux)?;
let mut last_was_wrapped = false;
let first_row = zone.start_y;
let last_row = zone.end_y;
fn cols_for_row(zone: &SemanticZone, row: StableRowIndex) -> std::ops::Range<usize> {
if row < zone.start_y || row > zone.end_y {
0..0
} else if zone.start_y == zone.end_y {
// A single line zone
if zone.start_x <= zone.end_x {
zone.start_x..zone.end_x.saturating_add(1)
} else {
zone.end_x..zone.start_x.saturating_add(1)
}
} else if row == zone.end_y {
// last line of multi-line
0..zone.end_x.saturating_add(1)
} else if row == zone.start_y {
// first line of multi-line
zone.start_x..usize::max_value()
} else {
// some "middle" line of multi-line
0..usize::max_value()
}
}
let mut s = String::new();
for line in pane.get_logical_lines(zone.start_y..zone.end_y + 1) {
if !s.is_empty() && !last_was_wrapped {
s.push('\n');
}
let last_idx = line.physical_lines.len().saturating_sub(1);
for (idx, phys) in line.physical_lines.iter().enumerate() {
let this_row = line.first_row + idx as StableRowIndex;
if this_row >= first_row && this_row < last_row {
let last_phys_idx = phys.len().saturating_sub(1);
let cols = cols_for_row(&zone, this_row);
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
// in a wrapped sequence
if idx == last_idx {
s.push_str(col_span.trim_end());
} else {
s.push_str(&col_span);
}
last_was_wrapped = last_col_idx == last_phys_idx
&& phys
.get_cell(last_col_idx)
.map(|c| c.attrs().wrapped())
.unwrap_or(false);
}
}
}
Ok(s)
}
}
impl UserData for MuxPane {
@ -209,6 +277,82 @@ impl UserData for MuxPane {
pane.perform_actions(actions);
Ok(())
});
methods.add_method("get_semantic_zones", |lua, this, of_type: Value| {
let mux = get_mux()?;
let pane = this.resolve(&mux)?;
let of_type: Option<SemanticType> = from_lua(of_type)?;
let mut zones = pane
.get_semantic_zones()
.map_err(|e| mlua::Error::external(format!("{:#}", e)))?;
if let Some(of_type) = of_type {
zones.retain(|zone| zone.semantic_type == of_type);
}
let zones = to_lua(lua, zones)?;
Ok(zones)
});
methods.add_method(
"get_semantic_zone_at",
|lua, this, (x, y): (usize, StableRowIndex)| {
let mux = get_mux()?;
let pane = this.resolve(&mux)?;
let zones = pane.get_semantic_zones().unwrap_or_else(|_| vec![]);
fn find_zone(x: usize, y: StableRowIndex, zone: &SemanticZone) -> Ordering {
match zone.start_y.cmp(&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(&x) {
Ordering::Greater => return Ordering::Greater,
Ordering::Equal | Ordering::Less => {}
},
Ordering::Less => {}
}
match zone.end_y.cmp(&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(&x) {
Ordering::Less => Ordering::Less,
Ordering::Equal | Ordering::Greater => Ordering::Equal,
},
Ordering::Greater => Ordering::Equal,
}
}
match zones.binary_search_by(|zone| find_zone(x, y, zone)) {
Ok(idx) => {
let zone = to_lua(lua, zones[idx])?;
Ok(Some(zone))
}
Err(_) => Ok(None),
}
},
);
methods.add_method("get_text_from_semantic_zone", |_lua, this, zone: Value| {
let zone: SemanticZone = from_lua(zone)?;
this.get_text_from_semantic_zone(zone)
});
methods.add_method("get_text_from_region", |_lua, this, (start_x, start_y, end_x, end_y): (usize, StableRowIndex, usize, StableRowIndex)| {
let zone = SemanticZone {
start_x,
start_y,
end_x,
end_y,
// semantic_type is not used by get_text_from_semantic_zone
semantic_type: SemanticType::Output,
};
this.get_text_from_semantic_zone(zone)
});
}
}

View File

@ -9,6 +9,28 @@ use wezterm_dynamic::{FromDynamic, ToDynamic, Value as DynValue};
pub mod enumctor;
pub fn to_lua<'lua, T: ToDynamic>(
lua: &'lua mlua::Lua,
value: T,
) -> Result<mlua::Value<'lua>, mlua::Error> {
let value = value.to_dynamic();
dynamic_to_lua_value(lua, value)
}
pub fn from_lua<'lua, T: FromDynamic>(value: mlua::Value<'lua>) -> Result<T, mlua::Error> {
let lua_type = value.type_name();
let value = lua_value_to_dynamic(value).map_err(|e| mlua::Error::FromLuaConversionError {
from: lua_type,
to: std::any::type_name::<T>(),
message: Some(e.to_string()),
})?;
T::from_dynamic(&value, Default::default()).map_err(|e| mlua::Error::FromLuaConversionError {
from: lua_type,
to: std::any::type_name::<T>(),
message: Some(e.to_string()),
})
}
/// Implement lua conversion traits for a type.
/// This implementation requires that the type implement
/// FromDynamic and ToDynamic.
@ -23,9 +45,7 @@ macro_rules! impl_lua_conversion_dynamic {
self,
lua: &'lua $crate::mlua::Lua,
) -> Result<$crate::mlua::Value<'lua>, $crate::mlua::Error> {
use wezterm_dynamic::ToDynamic;
let value = self.to_dynamic();
$crate::dynamic_to_lua_value(lua, value)
$crate::to_lua(lua, self)
}
}
@ -34,22 +54,7 @@ macro_rules! impl_lua_conversion_dynamic {
value: $crate::mlua::Value<'lua>,
_lua: &'lua $crate::mlua::Lua,
) -> Result<Self, $crate::mlua::Error> {
use wezterm_dynamic::FromDynamic;
let lua_type = value.type_name();
let value = $crate::lua_value_to_dynamic(value).map_err(|e| {
$crate::mlua::Error::FromLuaConversionError {
from: lua_type,
to: stringify!($struct),
message: Some(e.to_string()),
}
})?;
$struct::from_dynamic(&value, Default::default()).map_err(|e| {
$crate::mlua::Error::FromLuaConversionError {
from: lua_type,
to: stringify!($struct),
message: Some(e.to_string()),
}
})
$crate::from_lua(value)
}
}
};

View File

@ -21,6 +21,7 @@ use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut, Range};
use std::str;
use termwiz::surface::SequenceNo;
use wezterm_dynamic::{FromDynamic, ToDynamic};
pub mod config;
pub use config::TerminalConfiguration;
@ -115,7 +116,7 @@ pub struct CursorPosition {
}
#[cfg_attr(feature = "use_serde", derive(Deserialize, Serialize))]
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, FromDynamic, ToDynamic)]
pub struct SemanticZone {
pub start_y: StableRowIndex,
pub start_x: usize,