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:
parent
a2e027dff4
commit
15cd1afbdc
@ -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),
|
||||
|
30
docs/config/lua/pane/get_semantic_zone_at.md
Normal file
30
docs/config/lua/pane/get_semantic_zone_at.md
Normal 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.
|
||||
|
17
docs/config/lua/pane/get_semantic_zones.md
Normal file
17
docs/config/lua/pane/get_semantic_zones.md
Normal 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.
|
||||
|
16
docs/config/lua/pane/get_text_from_region.md
Normal file
16
docs/config/lua/pane/get_text_from_region.md
Normal 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.
|
||||
|
12
docs/config/lua/pane/get_text_from_semantic_zone.md
Normal file
12
docs/config/lua/pane/get_text_from_semantic_zone.md
Normal 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.
|
||||
|
@ -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)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user