This commit is contained in:
Antonio Scandurra 2023-10-24 13:10:37 +02:00
parent 953857f8e3
commit 9a53da28bc
9 changed files with 413 additions and 377 deletions

View file

@ -37,7 +37,7 @@ use terminal_settings::{AlternateScroll, Shell, TerminalBlink, TerminalSettings}
use util::truncate_and_trailoff;
use std::{
cmp::min,
cmp::{self, min},
collections::{HashMap, VecDeque},
fmt::Display,
ops::{Deref, Index, RangeInclusive},
@ -49,15 +49,12 @@ use std::{
use thiserror::Error;
use gpui2::{
px, AnyWindowHandle, AppContext, ClipboardItem, EventEmitter, Keystroke, MainThread,
ModelContext, Modifiers, MouseButton, MouseDragEvent, MouseScrollWheel, MouseUp, Pixels, Point,
Task, TouchPhase,
px, AnyWindowHandle, AppContext, Bounds, ClipboardItem, EventEmitter, Hsla, Keystroke,
MainThread, ModelContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
Pixels, Point, ScrollWheelEvent, Size, Task, TouchPhase,
};
use crate::mappings::{
colors::{get_color_at_index, to_alac_rgb},
keys::to_esc_str,
};
use crate::mappings::{colors::to_alac_rgb, keys::to_esc_str};
use lazy_static::lazy_static;
///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
@ -136,34 +133,32 @@ pub fn init(cx: &mut AppContext) {
pub struct TerminalSize {
pub cell_width: Pixels,
pub line_height: Pixels,
pub height: Pixels,
pub width: Pixels,
pub size: Size<Pixels>,
}
impl TerminalSize {
pub fn new(line_height: Pixels, cell_width: Pixels, size: Point<Pixels>) -> Self {
pub fn new(line_height: Pixels, cell_width: Pixels, size: Size<Pixels>) -> Self {
TerminalSize {
cell_width,
line_height,
width: size.x(),
height: size.y(),
size,
}
}
pub fn num_lines(&self) -> usize {
(self.height / self.line_height).floor() as usize
f32::from((self.size.height / self.line_height).floor()) as usize
}
pub fn num_columns(&self) -> usize {
(self.width / self.cell_width).floor() as usize
f32::from((self.size.width / self.cell_width).floor()) as usize
}
pub fn height(&self) -> Pixels {
self.height
self.size.height
}
pub fn width(&self) -> Pixels {
self.width
self.size.width
}
pub fn cell_width(&self) -> Pixels {
@ -179,7 +174,10 @@ impl Default for TerminalSize {
TerminalSize::new(
DEBUG_LINE_HEIGHT,
DEBUG_CELL_WIDTH,
Point::new(DEBUG_TERMINAL_WIDTH, DEBUG_TERMINAL_HEIGHT),
Size {
width: DEBUG_TERMINAL_WIDTH,
height: DEBUG_TERMINAL_HEIGHT,
},
)
}
}
@ -287,6 +285,7 @@ impl TerminalBuilder {
blink_settings: Option<TerminalBlink>,
alternate_scroll: AlternateScroll,
window: AnyWindowHandle,
color_for_index: impl Fn(usize, &mut AppContext) -> Hsla + Send + Sync + 'static,
) -> Result<TerminalBuilder> {
let pty_config = {
let alac_shell = match shell.clone() {
@ -392,6 +391,7 @@ impl TerminalBuilder {
selection_phase: SelectionPhase::Ended,
cmd_pressed: false,
hovered_word: false,
color_for_index: Box::new(color_for_index),
};
Ok(TerminalBuilder {
@ -402,7 +402,7 @@ impl TerminalBuilder {
pub fn subscribe(mut self, cx: &mut ModelContext<Terminal>) -> Terminal {
//Event loop
cx.spawn(|this, mut cx| async move {
cx.spawn_on_main(|this, mut cx| async move {
use futures::StreamExt;
while let Some(event) = self.events_rx.next().await {
@ -545,6 +545,8 @@ pub struct Terminal {
selection_phase: SelectionPhase,
cmd_pressed: bool,
hovered_word: bool,
// An implementation of the 8 bit ANSI color palette
color_for_index: Box<dyn Fn(usize, &mut AppContext) -> Hsla + Send + Sync + 'static>,
}
impl Terminal {
@ -627,19 +629,17 @@ impl Terminal {
) {
match event {
InternalEvent::ColorRequest(index, format) => {
let color = term.colors()[*index].unwrap_or_else(|| {
let term_style = &theme::current(cx).terminal;
to_alac_rgb(get_color_at_index(index, &term_style))
});
let color = term.colors()[*index]
.unwrap_or_else(|| to_alac_rgb((self.color_for_index)(*index, cx)));
self.write_to_pty(format(color))
}
InternalEvent::Resize(mut new_size) => {
new_size.height = f32::max(new_size.line_height, new_size.height);
new_size.width = f32::max(new_size.cell_width, new_size.width);
new_size.size.height = cmp::max(new_size.line_height, new_size.height());
new_size.size.width = cmp::max(new_size.cell_width, new_size.width());
self.last_content.size = new_size.clone();
self.pty_tx.0.send(Msg::Resize((new_size).into())).ok();
self.pty_tx.0.send(Msg::Resize(new_size.into())).ok();
term.resize(new_size);
}
@ -707,7 +707,8 @@ impl Terminal {
InternalEvent::Copy => {
if let Some(txt) = term.selection_to_string() {
cx.write_to_clipboard(ClipboardItem::new(txt))
cx.run_on_main(|cx| cx.write_to_clipboard(ClipboardItem::new(txt)))
.detach();
}
}
InternalEvent::ScrollToAlacPoint(point) => {
@ -952,11 +953,11 @@ impl Terminal {
}
pub fn try_modifiers_change(&mut self, modifiers: &Modifiers) -> bool {
let changed = self.cmd_pressed != modifiers.cmd;
if !self.cmd_pressed && modifiers.cmd {
let changed = self.cmd_pressed != modifiers.command;
if !self.cmd_pressed && modifiers.command {
self.refresh_hovered_word();
}
self.cmd_pressed = modifiers.cmd;
self.cmd_pressed = modifiers.command;
changed
}
@ -983,14 +984,14 @@ impl Terminal {
let delay = cx.executor().timer(Duration::from_millis(16));
self.sync_task = Some(cx.spawn(|weak_handle, mut cx| async move {
delay.await;
cx.update(|cx| {
if let Some(handle) = weak_handle.upgrade(cx) {
handle.update(cx, |terminal, cx| {
if let Some(handle) = weak_handle.upgrade() {
handle
.update(&mut cx, |terminal, cx| {
terminal.sync_task.take();
cx.notify();
});
}
});
})
.ok();
}
}));
return;
} else {
@ -1069,10 +1070,10 @@ impl Terminal {
self.last_content.mode.intersects(TermMode::MOUSE_MODE) && !shift
}
pub fn mouse_move(&mut self, e: &MouseMovedEvent, origin: Point<Pixels>) {
let position = e.position.sub(origin);
pub fn mouse_move(&mut self, e: &MouseMoveEvent, origin: Point<Pixels>) {
let position = e.position - origin;
self.last_mouse_position = Some(position);
if self.mouse_mode(e.shift) {
if self.mouse_mode(e.modifiers.shift) {
let point = grid_point(
position,
self.last_content.size,
@ -1099,11 +1100,11 @@ impl Terminal {
}
}
pub fn mouse_drag(&mut self, e: MouseDrag, origin: Point<Pixels>) {
let position = e.position.sub(origin);
pub fn mouse_drag(&mut self, e: MouseMoveEvent, origin: Point<Pixels>) {
let position = e.position - origin;
self.last_mouse_position = Some(position);
if !self.mouse_mode(e.shift) {
if !self.mouse_mode(e.modifiers.shift) {
self.selection_phase = SelectionPhase::Selecting;
// Alacritty has the same ordering, of first updating the selection
// then scrolling 15ms later
@ -1125,21 +1126,21 @@ impl Terminal {
}
}
fn drag_line_delta(&mut self, e: MouseDrag) -> Option<f32> {
fn drag_line_delta(&mut self, e: MouseMoveEvent, region: Bounds<Pixels>) -> Option<f32> {
//TODO: Why do these need to be doubled? Probably the same problem that the IME has
let top = e.region.origin_y() + (self.last_content.size.line_height * 2.);
let bottom = e.region.lower_left().y() - (self.last_content.size.line_height * 2.);
let scroll_delta = if e.position.y() < top {
(top - e.position.y()).powf(1.1)
} else if e.position.y() > bottom {
-((e.position.y() - bottom).powf(1.1))
let top = region.origin.y + (self.last_content.size.line_height * 2.);
let bottom = region.lower_left().y - (self.last_content.size.line_height * 2.);
let scroll_delta = if e.position.y < top {
(top - e.position.y).powf(1.1)
} else if e.position.y > bottom {
-((e.position.y - bottom).powf(1.1))
} else {
return None; //Nothing to do
};
Some(scroll_delta)
}
pub fn mouse_down(&mut self, e: &MouseDown, origin: Point<Pixels>) {
pub fn mouse_down(&mut self, e: &MouseDownEvent, origin: Point<Pixels>) {
let position = e.position.sub(origin);
let point = grid_point(
position,
@ -1148,7 +1149,9 @@ impl Terminal {
);
if self.mouse_mode(e.shift) {
if let Some(bytes) = mouse_button_report(point, e, true, self.last_content.mode) {
if let Some(bytes) =
mouse_button_report(point, e.button, e.modifiers, true, self.last_content.mode)
{
self.pty_tx.notify(bytes);
}
} else if e.button == MouseButton::Left {
@ -1180,7 +1183,12 @@ impl Terminal {
}
}
pub fn mouse_up(&mut self, e: &MouseUp, origin: Point<Pixels>, cx: &mut ModelContext<Self>) {
pub fn mouse_up(
&mut self,
e: &MouseUpEvent,
origin: Point<Pixels>,
cx: &mut ModelContext<Self>,
) {
let setting = settings2::get::<TerminalSettings>(cx);
let position = e.position.sub(origin);
@ -1191,7 +1199,9 @@ impl Terminal {
self.last_content.display_offset,
);
if let Some(bytes) = mouse_button_report(point, e, false, self.last_content.mode) {
if let Some(bytes) =
mouse_button_report(point, e.button, e.modifiers, false, self.last_content.mode)
{
self.pty_tx.notify(bytes);
}
} else {
@ -1274,7 +1284,7 @@ impl Terminal {
// Whenever we hit the edges, reset our stored scroll to 0
// so we can respond to changes in direction quickly
self.scroll_px %= self.last_content.size.height;
self.scroll_px %= self.last_content.size.height();
Some(new_offset - old_offset)
}
@ -1385,25 +1395,21 @@ fn all_search_matches<'a, T>(
RegexIter::new(start, end, AlacDirection::Right, term, regex)
}
fn content_index_for_mouse(pos: AlacPoint<Pixels>, size: &TerminalSize) -> usize {
let col = (pos.x() / size.cell_width()).round() as usize;
fn content_index_for_mouse(pos: Point<Pixels>, size: &TerminalSize) -> usize {
let col = (pos.x / size.cell_width()).round().as_usize();
let clamped_col = min(col, size.columns() - 1);
let row = (pos.y() / size.line_height()).round() as usize;
let row = (pos.y / size.line_height()).round().as_usize();
let clamped_row = min(row, size.screen_lines() - 1);
clamped_row * size.columns() + clamped_col
}
#[cfg(test)]
mod tests {
use alacritty_terminal::{
index::{AlacColumn, Line},
index::{AlacColumn, Line, Point as AlacPoint},
term::cell::Cell,
};
use gpui2::geometry::vecto::vec2f;
use gpui2::{geometry::vecto::vec2f, size, Pixels};
use rand::{distributions::Alphanumeric, rngs::ThreadRng, thread_rng, Rng};
use crate::{content_index_for_mouse, IndexedCell, TerminalContent, TerminalSize};
@ -1419,10 +1425,12 @@ mod tests {
let cell_size = rng.gen_range(5 * PRECISION..20 * PRECISION) as f32 / PRECISION as f32;
let size = crate::TerminalSize {
cell_width: cell_size,
line_height: cell_size,
height: cell_size * (viewport_cells as f32),
width: cell_size * (viewport_cells as f32),
cell_width: Pixels::from(cell_size),
line_height: Pixels::from(cell_size),
size: size(
Pixels::from(cell_size * (viewport_cells as f32)),
Pixels::from(cell_size * (viewport_cells as f32)),
),
};
let cells = get_cells(size, &mut rng);
@ -1456,10 +1464,9 @@ mod tests {
let mut rng = thread_rng();
let size = crate::TerminalSize {
cell_width: 10.,
line_height: 10.,
height: 100.,
width: 100.,
cell_width: Pixels::from(10.),
line_height: Pixels::from(10.),
size: size(Pixels::from(100.), Pixels::from(100.)),
};
let cells = get_cells(size, &mut rng);
@ -1478,9 +1485,9 @@ mod tests {
fn get_cells(size: TerminalSize, rng: &mut ThreadRng) -> Vec<Vec<char>> {
let mut cells = Vec::new();
for _ in 0..((size.height() / size.line_height()) as usize) {
for _ in 0..(f32::from(size.height() / size.line_height()) as usize) {
let mut row_vec = Vec::new();
for _ in 0..((size.width() / size.cell_width()) as usize) {
for _ in 0..(f32::from(size.width() / size.cell_width()) as usize) {
let cell_char = rng.sample(Alphanumeric) as char;
row_vec.push(cell_char)
}
@ -1497,7 +1504,7 @@ mod tests {
for col in 0..cells[row].len() {
let cell_char = cells[row][col];
ic.push(IndexedCell {
point: Point::new(Line(row as i32), Column(col)),
point: AlacPoint::new(Line(row as i32), Column(col)),
cell: Cell {
c: cell_char,
..Default::default()