mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-25 14:05:41 -05:00
Move selection into Grid
Supporting selections with scrollback has two major components: 1. Grid needs access to Selection so that it may update the scroll position as the terminal text changes. 2. Selection needs to be implemented in terms of buffer offsets -- NOT lines -- and be updated when Storage is rotated. This commit implements the first part.
This commit is contained in:
parent
69f533d19e
commit
2cfdd595f1
7 changed files with 68 additions and 53 deletions
|
@ -24,7 +24,6 @@ use config::Config;
|
|||
use font::{self, Rasterize};
|
||||
use meter::Meter;
|
||||
use renderer::{self, GlyphCache, QuadRenderer};
|
||||
use selection::Selection;
|
||||
use term::{Term, SizeInfo};
|
||||
|
||||
use window::{self, Size, Pixels, Window, SetInnerSize};
|
||||
|
@ -318,7 +317,7 @@ impl Display {
|
|||
/// A reference to Term whose state is being drawn must be provided.
|
||||
///
|
||||
/// This call may block if vsync is enabled
|
||||
pub fn draw(&mut self, mut terminal: MutexGuard<Term>, config: &Config, selection: Option<&Selection>) {
|
||||
pub fn draw(&mut self, mut terminal: MutexGuard<Term>, config: &Config) {
|
||||
// Clear dirty flag
|
||||
terminal.dirty = !terminal.visual_bell.completed();
|
||||
|
||||
|
@ -366,7 +365,7 @@ impl Display {
|
|||
|
||||
// Draw the grid
|
||||
api.render_cells(
|
||||
terminal.renderable_cells(config, selection, window_focused),
|
||||
terminal.renderable_cells(config, window_focused),
|
||||
glyph_cache,
|
||||
);
|
||||
});
|
||||
|
|
67
src/event.rs
67
src/event.rs
|
@ -33,10 +33,8 @@ pub trait Notify {
|
|||
pub struct ActionContext<'a, N: 'a> {
|
||||
pub notifier: &'a mut N,
|
||||
pub terminal: &'a mut Term,
|
||||
pub selection: &'a mut Option<Selection>,
|
||||
pub size_info: &'a SizeInfo,
|
||||
pub mouse: &'a mut Mouse,
|
||||
pub selection_modified: bool,
|
||||
pub received_count: &'a mut usize,
|
||||
pub suppress_chars: &'a mut bool,
|
||||
pub last_modifiers: &'a mut ModifiersState,
|
||||
|
@ -64,31 +62,35 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> {
|
|||
}
|
||||
|
||||
fn copy_selection(&self, buffer: ::copypasta::Buffer) {
|
||||
if let Some(ref selection) = *self.selection {
|
||||
selection.to_span(self.terminal)
|
||||
.map(|span| {
|
||||
let buf = self.terminal.string_from_selection(&span);
|
||||
if !buf.is_empty() {
|
||||
Clipboard::new()
|
||||
.and_then(|mut clipboard| clipboard.store(buf, buffer))
|
||||
.unwrap_or_else(|err| {
|
||||
warn!("Error storing selection to clipboard. {}", Red(err));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
self.terminal
|
||||
.selection_to_string()
|
||||
.map(|selected| {
|
||||
if !selected.is_empty() {
|
||||
Clipboard::new()
|
||||
.and_then(|mut clipboard| clipboard.store(selected, buffer))
|
||||
.unwrap_or_else(|err| {
|
||||
warn!("Error storing selection to clipboard. {}", Red(err));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn clear_selection(&mut self) {
|
||||
*self.selection = None;
|
||||
self.selection_modified = true;
|
||||
*self.terminal.selection_mut() = None;
|
||||
self.terminal.dirty = true;
|
||||
}
|
||||
|
||||
fn update_selection(&mut self, point: Point, side: Side) {
|
||||
self.selection_modified = true;
|
||||
|
||||
// Update selection if one exists
|
||||
if let Some(ref mut selection) = *self.selection {
|
||||
let mut had_selection = false; // borrowck
|
||||
if let Some(ref mut selection) = *self.terminal.selection_mut() {
|
||||
selection.update(point, side);
|
||||
had_selection = true;
|
||||
}
|
||||
|
||||
if had_selection { // borrowck
|
||||
self.terminal.dirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -97,18 +99,18 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> {
|
|||
}
|
||||
|
||||
fn simple_selection(&mut self, point: Point, side: Side) {
|
||||
*self.selection = Some(Selection::simple(point, side));
|
||||
self.selection_modified = true;
|
||||
*self.terminal.selection_mut() = Some(Selection::simple(point, side));
|
||||
self.terminal.dirty = true;
|
||||
}
|
||||
|
||||
fn semantic_selection(&mut self, point: Point) {
|
||||
*self.selection = Some(Selection::semantic(point, self.terminal));
|
||||
self.selection_modified = true;
|
||||
*self.terminal.selection_mut() = Some(Selection::semantic(point, &*self.terminal));
|
||||
self.terminal.dirty = true;
|
||||
}
|
||||
|
||||
fn line_selection(&mut self, point: Point) {
|
||||
*self.selection = Some(Selection::lines(point));
|
||||
self.selection_modified = true;
|
||||
*self.terminal.selection_mut() = Some(Selection::lines(point));
|
||||
self.terminal.dirty = true;
|
||||
}
|
||||
|
||||
fn mouse_coords(&self) -> Option<Point> {
|
||||
|
@ -197,7 +199,6 @@ pub struct Processor<N> {
|
|||
resize_tx: mpsc::Sender<(u32, u32)>,
|
||||
ref_test: bool,
|
||||
size_info: SizeInfo,
|
||||
pub selection: Option<Selection>,
|
||||
hide_cursor_when_typing: bool,
|
||||
hide_cursor: bool,
|
||||
received_count: usize,
|
||||
|
@ -212,7 +213,6 @@ pub struct Processor<N> {
|
|||
impl<N> OnResize for Processor<N> {
|
||||
fn on_resize(&mut self, size: &SizeInfo) {
|
||||
self.size_info = size.to_owned();
|
||||
self.selection = None;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,8 +239,7 @@ impl<N: Notify> Processor<N> {
|
|||
resize_tx,
|
||||
ref_test,
|
||||
mouse: Default::default(),
|
||||
selection: None,
|
||||
size_info,
|
||||
size_info: size_info,
|
||||
hide_cursor_when_typing: config.hide_cursor_when_typing(),
|
||||
hide_cursor: false,
|
||||
received_count: 0,
|
||||
|
@ -318,10 +317,6 @@ impl<N: Notify> Processor<N> {
|
|||
|
||||
*hide_cursor = false;
|
||||
processor.mouse_moved(x as u32, y as u32, modifiers);
|
||||
|
||||
if !processor.ctx.selection.is_none() {
|
||||
processor.ctx.terminal.dirty = true;
|
||||
}
|
||||
},
|
||||
MouseWheel { delta, phase, .. } => {
|
||||
*hide_cursor = false;
|
||||
|
@ -392,10 +387,8 @@ impl<N: Notify> Processor<N> {
|
|||
context = ActionContext {
|
||||
terminal: &mut terminal,
|
||||
notifier: &mut self.notifier,
|
||||
selection: &mut self.selection,
|
||||
mouse: &mut self.mouse,
|
||||
size_info: &self.size_info,
|
||||
selection_modified: false,
|
||||
received_count: &mut self.received_count,
|
||||
suppress_chars: &mut self.suppress_chars,
|
||||
last_modifiers: &mut self.last_modifiers,
|
||||
|
@ -440,10 +433,6 @@ impl<N: Notify> Processor<N> {
|
|||
}
|
||||
|
||||
window.is_focused = window_is_focused;
|
||||
|
||||
if processor.ctx.selection_modified {
|
||||
processor.ctx.terminal.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
self.wait_for_event = !terminal.dirty;
|
||||
|
|
|
@ -18,6 +18,7 @@ use std::cmp::{min, max, Ordering};
|
|||
use std::ops::{Deref, Range, Index, IndexMut, RangeTo, RangeFrom, RangeFull};
|
||||
|
||||
use index::{self, Point, Line, Column, IndexRange, RangeInclusive};
|
||||
use selection::Selection;
|
||||
|
||||
mod row;
|
||||
pub use self::row::Row;
|
||||
|
@ -54,8 +55,16 @@ impl<T> Deref for Indexed<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cols.eq(&other.cols) &&
|
||||
self.lines.eq(&other.lines) &&
|
||||
self.raw.eq(&other.raw)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the terminal display contents
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Grid<T> {
|
||||
/// Lines in the grid. Each row holds a list of cells corresponding to the
|
||||
/// columns in that row.
|
||||
|
@ -73,14 +82,13 @@ pub struct Grid<T> {
|
|||
///
|
||||
/// This is used to quickly populate new lines and clear recycled lines
|
||||
/// during scroll wrapping.
|
||||
#[serde(skip)]
|
||||
template_row: Row<T>,
|
||||
|
||||
/// Template cell for populating template_row
|
||||
#[serde(skip)]
|
||||
template: T,
|
||||
|
||||
/// Temporary row storage for scrolling with a region
|
||||
temp: Vec<Row<T>>,
|
||||
|
||||
/// Offset of displayed area
|
||||
///
|
||||
/// If the displayed region isn't at the bottom of the screen, it stays
|
||||
|
@ -90,6 +98,10 @@ pub struct Grid<T> {
|
|||
|
||||
/// An limit on how far back it's possible to scroll
|
||||
scroll_limit: usize,
|
||||
|
||||
/// Selected region
|
||||
#[serde(skip)]
|
||||
pub selection: Option<Selection>,
|
||||
}
|
||||
|
||||
pub struct GridIterator<'a, T: 'a> {
|
||||
|
@ -117,9 +129,9 @@ impl<T: Copy + Clone> Grid<T> {
|
|||
lines,
|
||||
template_row,
|
||||
template,
|
||||
temp: Vec::new(),
|
||||
display_offset: 0,
|
||||
scroll_limit: 0,
|
||||
selection: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ use std::slice;
|
|||
use index::Column;
|
||||
|
||||
/// A row in the grid
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct Row<T>(Vec<T>);
|
||||
|
||||
impl<T: Copy + Clone> Row<T> {
|
||||
|
|
|
@ -200,7 +200,7 @@ fn run(mut config: Config, options: &cli::Options) -> Result<(), Box<Error>> {
|
|||
display.handle_resize(&mut terminal, &config, &mut [&mut pty, &mut processor]);
|
||||
|
||||
// Draw the current state of the terminal
|
||||
display.draw(terminal, &config, processor.selection.as_ref());
|
||||
display.draw(terminal, &config);
|
||||
}
|
||||
|
||||
// Begin shutdown if the flag was raised.
|
||||
|
|
|
@ -39,6 +39,7 @@ use grid::ToRange;
|
|||
/// [`simple`]: enum.Selection.html#method.simple
|
||||
/// [`semantic`]: enum.Selection.html#method.semantic
|
||||
/// [`lines`]: enum.Selection.html#method.lines
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Selection {
|
||||
Simple {
|
||||
/// The region representing start and end of cursor movement
|
||||
|
@ -64,6 +65,7 @@ pub enum Selection {
|
|||
}
|
||||
|
||||
/// A Point and side within that point.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Anchor {
|
||||
point: Point,
|
||||
side: Side,
|
||||
|
|
|
@ -768,6 +768,14 @@ impl SizeInfo {
|
|||
|
||||
|
||||
impl Term {
|
||||
pub fn selection(&self) -> &Option<Selection> {
|
||||
&self.grid.selection
|
||||
}
|
||||
|
||||
pub fn selection_mut(&mut self) -> &mut Option<Selection> {
|
||||
&mut self.grid.selection
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_next_title(&mut self) -> Option<String> {
|
||||
self.next_title.take()
|
||||
|
@ -864,7 +872,7 @@ impl Term {
|
|||
self.dirty
|
||||
}
|
||||
|
||||
pub fn string_from_selection(&self, span: &Span) -> String {
|
||||
pub fn selection_to_string(&self) -> Option<String> {
|
||||
/// Need a generic push() for the Append trait
|
||||
trait PushChar {
|
||||
fn push_char(&mut self, c: char);
|
||||
|
@ -919,6 +927,9 @@ impl Term {
|
|||
}
|
||||
}
|
||||
|
||||
let selection = self.grid.selection.clone()?;
|
||||
let span = selection.to_span(self)?;
|
||||
|
||||
let mut res = String::new();
|
||||
|
||||
let (start, end) = span.to_locations();
|
||||
|
@ -955,7 +966,7 @@ impl Term {
|
|||
}
|
||||
}
|
||||
|
||||
res
|
||||
Some(res)
|
||||
}
|
||||
|
||||
/// Convert the given pixel values to a grid coordinate
|
||||
|
@ -984,10 +995,9 @@ impl Term {
|
|||
pub fn renderable_cells<'b>(
|
||||
&'b self,
|
||||
config: &'b Config,
|
||||
selection: Option<&'b Selection>,
|
||||
window_focused: bool,
|
||||
) -> RenderableCellsIter {
|
||||
let selection = selection.and_then(|s| s.to_span(self))
|
||||
let selection = self.grid.selection.as_ref().and_then(|s| s.to_span(self))
|
||||
.map(|span| span.to_range());
|
||||
let cursor = if window_focused {
|
||||
self.cursor_style.unwrap_or(self.default_cursor_style)
|
||||
|
@ -1029,6 +1039,9 @@ impl Term {
|
|||
return;
|
||||
}
|
||||
|
||||
self.grid.selection = None;
|
||||
self.alt_grid.selection = None;
|
||||
|
||||
// Should not allow less than 1 col, causes all sorts of checks to be required.
|
||||
if num_cols <= Column(1) {
|
||||
num_cols = Column(2);
|
||||
|
|
Loading…
Reference in a new issue