mirror of
https://github.com/wez/wezterm.git
synced 2024-09-20 03:09:06 +03:00
hyperlinks, selection by word and line deal with wrapping better
These now operate in terms of logical lines so they deal with lines that have wrapped outside the viewport better than in previous releases. closes: https://github.com/wez/wezterm/issues/408
This commit is contained in:
parent
0f23c140d1
commit
c6308202cb
@ -27,6 +27,7 @@ brief notes about them may accumulate here.
|
||||
* Fixed: X10 mouse coordinate reporting encoding could produce invalid outputs for large windows. Capped coordinate values to the maximum value that is representable in UTF-8 encoding
|
||||
* Fixed: font fallback now happens asynchronously from painting [#508](https://github.com/wez/wezterm/issues/508)
|
||||
* New: added [window:get_selection_text_for_pane](config/lua/window/get_selection_text_for_pane.md) method [#575](https://github.com/wez/wezterm/issues/575)
|
||||
* Fixed: implicit hyperlink rules, word and line selection now operate on logical lines which means that they deal with wrapped lines outside of the viewport. [#408](https://github.com/wez/wezterm/issues/408)
|
||||
|
||||
### 20210314-114017-04b7cedd
|
||||
|
||||
|
@ -78,6 +78,24 @@ pub struct LogicalLine {
|
||||
}
|
||||
|
||||
impl LogicalLine {
|
||||
pub fn xy_to_logical_x(&self, x: usize, y: StableRowIndex) -> usize {
|
||||
let mut offset = 0;
|
||||
for (idx, line) in self.physical_lines.iter().enumerate() {
|
||||
let phys_y = self.first_row + idx as StableRowIndex;
|
||||
if phys_y == y {
|
||||
return offset + x;
|
||||
}
|
||||
|
||||
offset += line.cells().len();
|
||||
}
|
||||
panic!(
|
||||
"x={} y={} is outside of this logical line starting at {} comprised of {} physical lines",
|
||||
x, y,
|
||||
self.first_row,
|
||||
self.physical_lines.len()
|
||||
);
|
||||
}
|
||||
|
||||
pub fn logical_x_to_physical_coord(&self, x: usize) -> (StableRowIndex, usize) {
|
||||
let mut y = self.first_row;
|
||||
let mut idx = 0;
|
||||
@ -109,8 +127,11 @@ impl LogicalLine {
|
||||
let num_phys = self.physical_lines.len();
|
||||
for (idx, phys) in self.physical_lines.iter_mut().enumerate() {
|
||||
let len = phys.cells().len();
|
||||
*phys = line.split_off(len);
|
||||
phys.set_last_cell_was_wrapped(idx == num_phys - 1);
|
||||
let remainder = line.split_off(len);
|
||||
*phys = line;
|
||||
line = remainder;
|
||||
let wrapped = idx == num_phys - 1;
|
||||
phys.set_last_cell_was_wrapped(wrapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -230,7 +251,11 @@ pub trait Pane: Downcast {
|
||||
}
|
||||
}
|
||||
|
||||
(first.unwrap(), phys_lines)
|
||||
if first.is_none() {
|
||||
assert_eq!(phys_lines.len(), 0);
|
||||
}
|
||||
|
||||
(first.unwrap_or(0), phys_lines)
|
||||
}
|
||||
|
||||
/// Returns render related dimensions
|
||||
|
@ -68,21 +68,17 @@ impl SelectionRange {
|
||||
|
||||
/// Computes the selection range for the line around the specified coords
|
||||
pub fn line_around(start: SelectionCoordinate, pane: &dyn Pane) -> Self {
|
||||
let mut end_y = start.y;
|
||||
loop {
|
||||
let next_y = end_y + 1;
|
||||
let (_, lines) = pane.get_lines(end_y..next_y);
|
||||
if !lines[0].last_cell_was_wrapped() {
|
||||
break;
|
||||
}
|
||||
end_y = next_y;
|
||||
}
|
||||
let logical = pane.get_logical_lines(start.y..start.y + 1);
|
||||
let logical = &logical[0];
|
||||
|
||||
Self {
|
||||
start: SelectionCoordinate { x: 0, y: start.y },
|
||||
start: SelectionCoordinate {
|
||||
x: 0,
|
||||
y: logical.first_row,
|
||||
},
|
||||
end: SelectionCoordinate {
|
||||
x: usize::max_value(),
|
||||
y: end_y,
|
||||
y: logical.first_row + (logical.physical_lines.len() - 1) as StableRowIndex,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -135,62 +131,30 @@ impl SelectionRange {
|
||||
|
||||
/// Computes the selection range for the word around the specified coords
|
||||
pub fn word_around(start: SelectionCoordinate, pane: &dyn Pane) -> Self {
|
||||
let (first, lines) = pane.get_lines(start.y..start.y + 1);
|
||||
|
||||
// TODO: if selection_range.start.x == 0, search backwards for wrapping
|
||||
// lines too.
|
||||
|
||||
match lines[0].compute_double_click_range(start.x, is_double_click_word) {
|
||||
DoubleClickRange::Range(click_range) => Self {
|
||||
start: SelectionCoordinate {
|
||||
x: click_range.start,
|
||||
y: first,
|
||||
},
|
||||
end: SelectionCoordinate {
|
||||
x: click_range.end - 1,
|
||||
y: first,
|
||||
},
|
||||
},
|
||||
DoubleClickRange::RangeWithWrap(range_start) => {
|
||||
let start_coord = SelectionCoordinate {
|
||||
x: range_start.start,
|
||||
y: first,
|
||||
};
|
||||
|
||||
let mut end_coord = SelectionCoordinate {
|
||||
x: range_start.end - 1,
|
||||
y: first,
|
||||
};
|
||||
|
||||
for y_cont in start.y + 1.. {
|
||||
let (first, lines) = pane.get_lines(y_cont..y_cont + 1);
|
||||
if first != y_cont {
|
||||
break;
|
||||
}
|
||||
match lines[0].compute_double_click_range(0, is_double_click_word) {
|
||||
DoubleClickRange::Range(range_end) => {
|
||||
if range_end.end > range_end.start {
|
||||
end_coord = SelectionCoordinate {
|
||||
x: range_end.end - 1,
|
||||
y: y_cont,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
DoubleClickRange::RangeWithWrap(range_end) => {
|
||||
end_coord = SelectionCoordinate {
|
||||
x: range_end.end - 1,
|
||||
y: y_cont,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
let logical = pane.get_logical_lines(start.y..start.y + 1);
|
||||
let logical = &logical[0];
|
||||
|
||||
let start_idx = logical.xy_to_logical_x(start.x, start.y);
|
||||
match logical
|
||||
.logical
|
||||
.compute_double_click_range(start_idx, is_double_click_word)
|
||||
{
|
||||
DoubleClickRange::Range(click_range) => {
|
||||
let (start_y, start_x) = logical.logical_x_to_physical_coord(click_range.start);
|
||||
let (end_y, end_x) = logical.logical_x_to_physical_coord(click_range.end - 1);
|
||||
Self {
|
||||
start: start_coord,
|
||||
end: end_coord,
|
||||
start: SelectionCoordinate {
|
||||
x: start_x,
|
||||
y: start_y,
|
||||
},
|
||||
end: SelectionCoordinate { x: end_x, y: end_y },
|
||||
}
|
||||
}
|
||||
DoubleClickRange::RangeWithWrap(_) => {
|
||||
// We're using logical lines to match against, so we should never get
|
||||
// a RangeWithWrap result here
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -358,7 +358,10 @@ impl super::TermWindow {
|
||||
|
||||
self.last_mouse_terminal_coords = (x, stable_row); // FIXME: per-pane
|
||||
|
||||
let (top, mut lines) = pane.get_lines(stable_row..stable_row + 1);
|
||||
let (top, mut lines) = pane.get_lines_with_hyperlinks_applied(
|
||||
stable_row..stable_row + 1,
|
||||
&self.config.hyperlink_rules,
|
||||
);
|
||||
let new_highlight = if top == stable_row {
|
||||
if let Some(line) = lines.get_mut(0) {
|
||||
if let Some(cell) = line.cells().get(x) {
|
||||
|
@ -18,7 +18,6 @@ use mux::renderable::{RenderableDimensions, StableCursorPosition};
|
||||
use mux::tab::{PositionedPane, PositionedSplit, SplitDirection};
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use termwiz::cellcluster::CellCluster;
|
||||
use termwiz::surface::{CursorShape, CursorVisibility};
|
||||
@ -172,12 +171,9 @@ impl super::TermWindow {
|
||||
None => dims.physical_top..dims.physical_top + dims.viewport_rows as StableRowIndex,
|
||||
};
|
||||
|
||||
/*
|
||||
let (top, vp_lines) = pos
|
||||
.pane
|
||||
.get_lines_with_hyperlinks_applied(stable_range, &self.config.hyperlink_rules);
|
||||
*/
|
||||
let (top, vp_lines) = pos.pane.get_lines(stable_range);
|
||||
stable_top = top;
|
||||
lines = vp_lines;
|
||||
}
|
||||
@ -695,7 +691,7 @@ impl super::TermWindow {
|
||||
let attrs = &cluster.attrs;
|
||||
|
||||
let is_highlited_hyperlink = match (attrs.hyperlink(), &self.current_highlight) {
|
||||
(Some(ref this), &Some(ref highlight)) => Arc::ptr_eq(this, highlight),
|
||||
(Some(ref this), &Some(ref highlight)) => **this == *highlight,
|
||||
_ => false,
|
||||
};
|
||||
let style = self.fonts.match_style(params.config, attrs);
|
||||
|
@ -14,7 +14,8 @@ impl super::TermWindow {
|
||||
.map(|r| r.normalize())
|
||||
{
|
||||
let mut last_was_wrapped = false;
|
||||
let (first_row, lines) = pane.get_lines(sel.rows());
|
||||
let (first_row, lines) =
|
||||
pane.get_lines_with_hyperlinks_applied(sel.rows(), &self.config.hyperlink_rules);
|
||||
for (idx, line) in lines.iter().enumerate() {
|
||||
let cols = sel.cols_for_row(first_row + idx as StableRowIndex);
|
||||
let last_col_idx = cols.end.min(line.cells().len()).saturating_sub(1);
|
||||
|
Loading…
Reference in New Issue
Block a user