diff options
Diffstat (limited to 'alacritty')
-rw-r--r-- | alacritty/src/config/window.rs | 6 | ||||
-rw-r--r-- | alacritty/src/display/content.rs | 63 | ||||
-rw-r--r-- | alacritty/src/display/cursor.rs | 2 | ||||
-rw-r--r-- | alacritty/src/display/hint.rs | 4 | ||||
-rw-r--r-- | alacritty/src/display/mod.rs | 58 | ||||
-rw-r--r-- | alacritty/src/event.rs | 161 | ||||
-rw-r--r-- | alacritty/src/input.rs | 163 | ||||
-rw-r--r-- | alacritty/src/main.rs | 3 | ||||
-rw-r--r-- | alacritty/src/message_bar.rs | 6 | ||||
-rw-r--r-- | alacritty/src/renderer/mod.rs | 5 | ||||
-rw-r--r-- | alacritty/src/renderer/rects.rs | 17 | ||||
-rw-r--r-- | alacritty/src/url.rs | 65 |
12 files changed, 229 insertions, 324 deletions
diff --git a/alacritty/src/config/window.rs b/alacritty/src/config/window.rs index 2d501b91..d74390d8 100644 --- a/alacritty/src/config/window.rs +++ b/alacritty/src/config/window.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Deserializer}; use alacritty_config_derive::ConfigDeserialize; use alacritty_terminal::config::LOG_TARGET_CONFIG; -use alacritty_terminal::index::{Column, Line}; +use alacritty_terminal::index::Column; use crate::config::ui_config::Delta; @@ -74,7 +74,7 @@ impl WindowConfig { #[inline] pub fn dimensions(&self) -> Option<Dimensions> { if self.dimensions.columns.0 != 0 - && self.dimensions.lines.0 != 0 + && self.dimensions.lines != 0 && self.startup_mode != StartupMode::Maximized { Some(self.dimensions) @@ -145,7 +145,7 @@ pub struct Dimensions { pub columns: Column, /// Window Height in character lines. - pub lines: Line, + pub lines: usize, } /// Window class hint. diff --git a/alacritty/src/display/content.rs b/alacritty/src/display/content.rs index a25ddce8..a793f443 100644 --- a/alacritty/src/display/content.rs +++ b/alacritty/src/display/content.rs @@ -1,5 +1,5 @@ use std::borrow::Cow; -use std::cmp::max; +use std::cmp::{max, min}; use std::mem; use std::ops::{Deref, DerefMut, RangeInclusive}; @@ -126,12 +126,17 @@ impl<'a> RenderableContent<'a> { let text_color = text_color.color(cell.fg, cell.bg); let cursor_color = cursor_color.color(cell.fg, cell.bg); + // Convert cursor point to viewport position. + let cursor_point = self.terminal_cursor.point; + let line = (cursor_point.line + self.terminal_content.display_offset as i32).0 as usize; + let point = Point::new(line, cursor_point.column); + Some(RenderableCursor { - point: self.terminal_cursor.point, shape: self.terminal_cursor.shape, cursor_color, text_color, is_wide, + point, }) } } @@ -147,9 +152,10 @@ impl<'a> Iterator for RenderableContent<'a> { fn next(&mut self) -> Option<Self::Item> { loop { let cell = self.terminal_content.display_iter.next()?; + let cell_point = cell.point; let mut cell = RenderableCell::new(self, cell); - if self.terminal_cursor.point == cell.point { + if self.terminal_cursor.point == cell_point { // Store the cursor which should be rendered. self.cursor = self.renderable_cursor(&cell).map(|cursor| { if cursor.shape == CursorShape::Block { @@ -178,16 +184,15 @@ impl<'a> Iterator for RenderableContent<'a> { pub struct RenderableCell { pub character: char, pub zerowidth: Option<Vec<char>>, - pub point: Point, + pub point: Point<usize>, pub fg: Rgb, pub bg: Rgb, pub bg_alpha: f32, pub flags: Flags, - pub is_match: bool, } impl RenderableCell { - fn new<'a>(content: &mut RenderableContent<'a>, cell: Indexed<&Cell, Line>) -> Self { + fn new<'a>(content: &mut RenderableContent<'a>, cell: Indexed<&Cell>) -> Self { // Lookup RGB values. let mut fg_rgb = Self::compute_fg_rgb(content, cell.fg, cell.flags); let mut bg_rgb = Self::compute_bg_rgb(content, cell.bg); @@ -203,7 +208,6 @@ impl RenderableCell { .terminal_content .selection .map_or(false, |selection| selection.contains_cell(&cell, content.terminal_cursor)); - let mut is_match = false; let mut character = cell.c; @@ -233,19 +237,21 @@ impl RenderableCell { let config_fg = colors.search.matches.foreground; let config_bg = colors.search.matches.background; Self::compute_cell_rgb(&mut fg_rgb, &mut bg_rgb, &mut bg_alpha, config_fg, config_bg); - - is_match = true; } + // Convert cell point to viewport position. + let cell_point = cell.point; + let line = (cell_point.line + content.terminal_content.display_offset as i32).0 as usize; + let point = Point::new(line, cell_point.column); + RenderableCell { - character, zerowidth: cell.zerowidth().map(|zerowidth| zerowidth.to_vec()), - point: cell.point, + flags: cell.flags, fg: fg_rgb, bg: bg_rgb, + character, bg_alpha, - flags: cell.flags, - is_match, + point, } } @@ -349,7 +355,7 @@ pub struct RenderableCursor { cursor_color: Rgb, text_color: Rgb, is_wide: bool, - point: Point, + point: Point<usize>, } impl RenderableCursor { @@ -365,7 +371,7 @@ impl RenderableCursor { self.is_wide } - pub fn point(&self) -> Point { + pub fn point(&self) -> Point<usize> { self.point } } @@ -423,36 +429,23 @@ pub struct RegexMatches(Vec<RangeInclusive<Point>>); impl RegexMatches { /// Find all visible matches. pub fn new<T>(term: &Term<T>, dfas: &RegexSearch) -> Self { - let viewport_end = term.grid().display_offset(); - let viewport_start = viewport_end + term.screen_lines().0 - 1; + let viewport_start = Line(-(term.grid().display_offset() as i32)); + let viewport_end = viewport_start + term.bottommost_line(); // Compute start of the first and end of the last line. let start_point = Point::new(viewport_start, Column(0)); let mut start = term.line_search_left(start_point); - let end_point = Point::new(viewport_end, term.cols() - 1); + let end_point = Point::new(viewport_end, term.last_column()); let mut end = term.line_search_right(end_point); // Set upper bound on search before/after the viewport to prevent excessive blocking. - if start.line > viewport_start + MAX_SEARCH_LINES { - if start.line == 0 { - // Do not highlight anything if this line is the last. - return Self::default(); - } else { - // Start at next line if this one is too long. - start.line -= 1; - } - } - end.line = max(end.line, viewport_end.saturating_sub(MAX_SEARCH_LINES)); + start.line = max(start.line, viewport_start - MAX_SEARCH_LINES); + end.line = min(end.line, viewport_end + MAX_SEARCH_LINES); // Create an iterater for the current regex search for all visible matches. let iter = RegexIter::new(start, end, Direction::Right, term, dfas) - .skip_while(move |rm| rm.end().line > viewport_start) - .take_while(move |rm| rm.start().line >= viewport_end) - .map(|rm| { - let viewport_start = term.grid().clamp_buffer_to_visible(*rm.start()); - let viewport_end = term.grid().clamp_buffer_to_visible(*rm.end()); - viewport_start..=viewport_end - }); + .skip_while(move |rm| rm.end().line < viewport_start) + .take_while(move |rm| rm.start().line <= viewport_end); Self(iter.collect()) } diff --git a/alacritty/src/display/cursor.rs b/alacritty/src/display/cursor.rs index 7cd631e3..f3a782cc 100644 --- a/alacritty/src/display/cursor.rs +++ b/alacritty/src/display/cursor.rs @@ -17,7 +17,7 @@ impl IntoRects for RenderableCursor { fn rects(self, size_info: &SizeInfo, thickness: f32) -> CursorRects { let point = self.point(); let x = point.column.0 as f32 * size_info.cell_width() + size_info.padding_x(); - let y = point.line.0 as f32 * size_info.cell_height() + size_info.padding_y(); + let y = point.line as f32 * size_info.cell_height() + size_info.padding_y(); let mut width = size_info.cell_width(); let height = size_info.cell_height(); diff --git a/alacritty/src/display/hint.rs b/alacritty/src/display/hint.rs index 6499a959..fe107139 100644 --- a/alacritty/src/display/hint.rs +++ b/alacritty/src/display/hint.rs @@ -118,9 +118,7 @@ impl HintState { if label.len() == 1 { // Get text for the hint's regex match. let hint_match = &self.matches[index]; - let start = term.visible_to_buffer(*hint_match.start()); - let end = term.visible_to_buffer(*hint_match.end()); - let text = term.bounds_to_string(start, end); + let text = term.bounds_to_string(*hint_match.start(), *hint_match.end()); // Append text as last argument and launch command. let program = hint.command.program(); diff --git a/alacritty/src/display/mod.rs b/alacritty/src/display/mod.rs index cbf2930a..0f20e45f 100644 --- a/alacritty/src/display/mod.rs +++ b/alacritty/src/display/mod.rs @@ -27,7 +27,7 @@ use alacritty_terminal::event::{EventListener, OnResize}; use alacritty_terminal::grid::Dimensions as _; use alacritty_terminal::index::{Column, Direction, Line, Point}; use alacritty_terminal::selection::Selection; -use alacritty_terminal::term::{SizeInfo, Term, TermMode, MIN_COLS, MIN_SCREEN_LINES}; +use alacritty_terminal::term::{SizeInfo, Term, TermMode, MIN_COLUMNS, MIN_SCREEN_LINES}; use crate::config::font::Font; use crate::config::window::Dimensions; @@ -477,12 +477,6 @@ impl Display { mods: ModifiersState, search_state: &SearchState, ) { - // Convert search match from viewport to absolute indexing. - let search_active = search_state.regex().is_some(); - let viewport_match = search_state - .focused_match() - .and_then(|focused_match| terminal.grid().clamp_buffer_range_to_visible(focused_match)); - // Collect renderable content before the terminal is dropped. let mut content = RenderableContent::new(config, self, &terminal, search_state); let mut grid_cells = Vec::new(); @@ -522,13 +516,15 @@ impl Display { let glyph_cache = &mut self.glyph_cache; self.renderer.with_api(&config.ui_config, &size_info, |mut api| { // Iterate over all non-empty cells in the grid. + let focused_match = search_state.focused_match(); for mut cell in grid_cells { - // Invert the active match during search. - if cell.is_match - && viewport_match - .as_ref() - .map_or(false, |viewport_match| viewport_match.contains(&cell.point)) - { + let focused = focused_match.as_ref().map_or(false, |focused_match| { + let line = Line(cell.point.line as i32) - display_offset; + focused_match.contains(&Point::new(line, cell.point.column)) + }); + + // Invert the focused match during search. + if focused { let colors = config.ui_config.colors.search.focused_match; let match_fg = colors.foreground.color(cell.fg, cell.bg); cell.bg = colors.background.color(cell.fg, cell.bg); @@ -537,7 +533,7 @@ impl Display { } // Update URL underlines. - urls.update(size_info.cols(), &cell); + urls.update(&size_info, &cell); // Update underline/strikeout. lines.update(&cell); @@ -570,15 +566,16 @@ impl Display { if let Some(vi_mode_cursor) = vi_mode_cursor { // Highlight URLs at the vi mode cursor position. - let vi_mode_point = vi_mode_cursor.point; - if let Some(url) = self.urls.find_at(vi_mode_point) { + let vi_point = vi_mode_cursor.point; + let line = (vi_point.line + display_offset).0 as usize; + if let Some(url) = self.urls.find_at(Point::new(line, vi_point.column)) { rects.append(&mut url.rects(&metrics, &size_info)); } // Indicate vi mode by showing the cursor's position in the top right corner. - let line = size_info.screen_lines() + display_offset - vi_mode_point.line - 1; - self.draw_line_indicator(config, &size_info, total_lines, Some(vi_mode_point), line.0); - } else if search_active { + let line = (-vi_point.line.0 + size_info.bottommost_line().0) as usize; + self.draw_line_indicator(config, &size_info, total_lines, Some(vi_point), line); + } else if search_state.regex().is_some() { // Show current display offset in vi-less search to indicate match position. self.draw_line_indicator(config, &size_info, total_lines, None, display_offset); } @@ -605,12 +602,12 @@ impl Display { } if let Some(message) = message_buffer.message() { - let search_offset = if search_active { 1 } else { 0 }; + let search_offset = if search_state.regex().is_some() { 1 } else { 0 }; let text = message.text(&size_info); // Create a new rectangle for the background. let start_line = size_info.screen_lines() + search_offset; - let y = size_info.cell_height().mul_add(start_line.0 as f32, size_info.padding_y()); + let y = size_info.cell_height().mul_add(start_line as f32, size_info.padding_y()); let bg = match message.ty() { MessageType::Error => config.ui_config.colors.normal.red, @@ -656,7 +653,8 @@ impl Display { self.draw_search(config, &size_info, &search_text); // Compute IME position. - Point::new(size_info.screen_lines() + 1, Column(search_text.chars().count() - 1)) + let line = Line(size_info.screen_lines() as i32 + 1); + Point::new(line, Column(search_text.chars().count() - 1)) }, None => cursor_point, }; @@ -703,7 +701,7 @@ impl Display { formatted_regex.push('_'); // Truncate beginning of the search regex if it exceeds the viewport width. - let num_cols = size_info.cols().0; + let num_cols = size_info.columns(); let label_len = search_label.chars().count(); let regex_len = formatted_regex.chars().count(); let truncate_len = min((regex_len + label_len).saturating_sub(num_cols), regex_len); @@ -722,7 +720,7 @@ impl Display { /// Draw current search regex. fn draw_search(&mut self, config: &Config, size_info: &SizeInfo, text: &str) { let glyph_cache = &mut self.glyph_cache; - let num_cols = size_info.cols().0; + let num_cols = size_info.columns(); // Assure text length is at least num_cols. let text = format!("{:<1$}", text, num_cols); @@ -745,7 +743,7 @@ impl Display { let glyph_cache = &mut self.glyph_cache; let timing = format!("{:.3} usec", self.meter.average()); - let point = Point::new(size_info.screen_lines() - 2, Column(0)); + let point = Point::new(size_info.screen_lines().saturating_sub(2), Column(0)); let fg = config.ui_config.colors.primary.background; let bg = config.ui_config.colors.normal.red; @@ -764,16 +762,16 @@ impl Display { line: usize, ) { let text = format!("[{}/{}]", line, total_lines - 1); - let column = Column(size_info.cols().0.saturating_sub(text.len())); + let column = Column(size_info.columns().saturating_sub(text.len())); let colors = &config.ui_config.colors; let fg = colors.line_indicator.foreground.unwrap_or(colors.primary.background); let bg = colors.line_indicator.background.unwrap_or(colors.primary.foreground); // Do not render anything if it would obscure the vi mode cursor. - if vi_mode_point.map_or(true, |point| point.line.0 != 0 || point.column < column) { + if vi_mode_point.map_or(true, |point| point.line != 0 || point.column < column) { let glyph_cache = &mut self.glyph_cache; self.renderer.with_api(&config.ui_config, &size_info, |mut api| { - api.render_string(glyph_cache, Point::new(Line(0), column), fg, bg, &text); + api.render_string(glyph_cache, Point::new(0, column), fg, bg, &text); }); } } @@ -822,8 +820,8 @@ fn window_size( ) -> PhysicalSize<u32> { let padding = config.ui_config.window.padding(dpr); - let grid_width = cell_width * dimensions.columns.0.max(MIN_COLS) as f32; - let grid_height = cell_height * dimensions.lines.0.max(MIN_SCREEN_LINES) as f32; + let grid_width = cell_width * dimensions.columns.0.max(MIN_COLUMNS) as f32; + let grid_height = cell_height * dimensions.lines.max(MIN_SCREEN_LINES) as f32; let width = (padding.0).mul_add(2., grid_width).floor(); let height = (padding.1).mul_add(2., grid_height).floor(); diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index 7faf380e..94c40a39 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -11,7 +11,6 @@ use std::fs; use std::fs::File; use std::io::Write; use std::mem; -use std::ops::RangeInclusive; use std::path::{Path, PathBuf}; #[cfg(not(any(target_os = "macos", windows)))] use std::sync::atomic::Ordering; @@ -93,13 +92,13 @@ pub struct SearchState { direction: Direction, /// Change in display offset since the beginning of the search. - display_offset_delta: isize, + display_offset_delta: i32, /// Search origin in viewport coordinates relative to original display offset. origin: Point, /// Focused match during active search. - focused_match: Option<RangeInclusive<Point<usize>>>, + focused_match: Option<Match>, /// Search regex and history. /// @@ -128,7 +127,7 @@ impl SearchState { } /// Focused match during vi-less search. - pub fn focused_match(&self) -> Option<&RangeInclusive<Point<usize>>> { + pub fn focused_match(&self) -> Option<&Match> { self.focused_match.as_ref() } @@ -195,14 +194,14 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon } fn scroll(&mut self, scroll: Scroll) { - let old_offset = self.terminal.grid().display_offset() as isize; + let old_offset = self.terminal.grid().display_offset() as i32; self.terminal.scroll_display(scroll); // Keep track of manual display offset changes during search. if self.search_active() { let display_offset = self.terminal.grid().display_offset(); - self.search_state.display_offset_delta += old_offset - display_offset as isize; + self.search_state.display_offset_delta += old_offset - display_offset as i32; } // Update selection. @@ -213,9 +212,10 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon } else if self.mouse().left_button_state == ElementState::Pressed || self.mouse().right_button_state == ElementState::Pressed { - let point = self.size_info().pixels_to_coords(self.mouse().x, self.mouse().y); - let cell_side = self.mouse().cell_side; - self.update_selection(point, cell_side); + let point = self.mouse().point; + let line = Line(point.line as i32) - self.terminal.grid().display_offset(); + let point = Point::new(line, point.column); + self.update_selection(point, self.mouse().cell_side); } *self.dirty = true; @@ -245,11 +245,10 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon }; // Treat motion over message bar like motion over the last line. - point.line = min(point.line, self.terminal.screen_lines() - 1); + point.line = min(point.line, self.terminal.bottommost_line()); // Update selection. - let absolute_point = self.terminal.visible_to_buffer(point); - selection.update(absolute_point, side); + selection.update(point, side); // Move vi cursor and expand selection. if self.terminal.mode().contains(TermMode::VI) && !self.search_active() { @@ -262,7 +261,6 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon } fn start_selection(&mut self, ty: SelectionType, point: Point, side: Side) { - let point = self.terminal.visible_to_buffer(point); self.terminal.selection = Some(Selection::new(ty, point, side)); *self.dirty = true; } @@ -280,17 +278,6 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon } } - fn mouse_coords(&self) -> Option<Point> { - let x = self.mouse.x as usize; - let y = self.mouse.y as usize; - - if self.display.size_info.contains_point(x, y) { - Some(self.display.size_info.pixels_to_coords(x, y)) - } else { - None - } - } - #[inline] fn mouse_mode(&self) -> bool { self.terminal.mode().intersects(TermMode::MOUSE_MODE) @@ -393,9 +380,13 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon } if let Some(ref launcher) = self.config.ui_config.mouse.url.launcher { + let display_offset = self.terminal.grid().display_offset(); + let start = url.start(); + let start = Point::new(Line(start.line as i32 - display_offset as i32), start.column); + let end = url.end(); + let end = Point::new(Line(end.line as i32 - display_offset as i32), end.column); + let mut args = launcher.args().to_vec(); - let start = self.terminal.visible_to_buffer(url.start()); - let end = self.terminal.visible_to_buffer(url.end()); args.push(self.terminal.bounds_to_string(start, end)); start_daemon(launcher.program(), &args); @@ -430,9 +421,6 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon #[inline] fn start_search(&mut self, direction: Direction) { - let num_lines = self.terminal.screen_lines(); - let num_cols = self.terminal.cols(); - // Only create new history entry if the previous regex wasn't empty. if self.search_state.history.get(0).map_or(true, |regex| !regex.is_empty()) { self.search_state.history.push_front(String::new()); @@ -448,12 +436,12 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon self.search_state.origin = self.terminal.vi_mode_cursor.point; self.search_state.display_offset_delta = 0; } else { - match direction { - Direction::Right => self.search_state.origin = Point::new(Line(0), Column(0)), - Direction::Left => { - self.search_state.origin = Point::new(num_lines - 2, num_cols - 1); - }, - } + let screen_lines = self.terminal.screen_lines(); + let last_column = self.terminal.last_column(); + self.search_state.origin = match direction { + Direction::Right => Point::new(Line(0), Column(0)), + Direction::Left => Point::new(Line(screen_lines as i32 - 2), last_column), + }; } self.display_update_pending.dirty = true; @@ -483,8 +471,8 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon self.search_reset_state(); } else if let Some(focused_match) = &self.search_state.focused_match { // Create a selection for the focused match. - let start = self.terminal.grid().clamp_buffer_to_visible(*focused_match.start()); - let end = self.terminal.grid().clamp_buffer_to_visible(*focused_match.end()); + let start = *focused_match.start(); + let end = *focused_match.end(); self.start_selection(SelectionType::Simple, start, Side::Left); self.update_selection(end, Side::Right); } @@ -565,19 +553,14 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon // Use focused match as new search origin if available. if let Some(focused_match) = &self.search_state.focused_match { let new_origin = match direction { - Direction::Right => { - focused_match.end().add_absolute(self.terminal, Boundary::Wrap, 1) - }, - Direction::Left => { - focused_match.start().sub_absolute(self.terminal, Boundary::Wrap, 1) - }, + Direction::Right => focused_match.end().add(self.terminal, Boundary::None, 1), + Direction::Left => focused_match.start().sub(self.terminal, Boundary::None, 1), }; self.terminal.scroll_to_point(new_origin); - let origin_relative = self.terminal.grid().clamp_buffer_to_visible(new_origin); - self.search_state.origin = origin_relative; self.search_state.display_offset_delta = 0; + self.search_state.origin = new_origin; } // Search for the next match using the supplied direction. @@ -600,24 +583,18 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon }; // Store the search origin with display offset by checking how far we need to scroll to it. - let old_display_offset = self.terminal.grid().display_offset() as isize; + let old_display_offset = self.terminal.grid().display_offset() as i32; self.terminal.scroll_to_point(new_origin); - let new_display_offset = self.terminal.grid().display_offset() as isize; + let new_display_offset = self.terminal.grid().display_offset() as i32; self.search_state.display_offset_delta = new_display_offset - old_display_offset; // Store origin and scroll back to the match. - let origin_relative = self.terminal.grid().clamp_buffer_to_visible(new_origin); self.terminal.scroll_display(Scroll::Delta(-self.search_state.display_offset_delta)); - self.search_state.origin = origin_relative; + self.search_state.origin = new_origin; } /// Find the next search match. - fn search_next( - &mut self, - origin: Point<usize>, - direction: Direction, - side: Side, - ) -> Option<Match> { + fn search_next(&mut self, origin: Point, direction: Direction, side: Side) -> Option<Match> { self.search_state .dfas .as_ref() @@ -746,15 +723,10 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { return; } - // Reset display offset. + // Reset display offset and cursor position. self.terminal.scroll_display(Scroll::Delta(self.search_state.display_offset_delta)); self.search_state.display_offset_delta = 0; - - // Reset vi mode cursor. - let mut origin = self.search_state.origin; - origin.line = min(origin.line, self.terminal.screen_lines() - 1); - origin.column = min(origin.column, self.terminal.cols() - 1); - self.terminal.vi_mode_cursor.point = origin; + self.terminal.vi_mode_cursor.point = self.search_state.origin; *self.dirty = true; } @@ -771,10 +743,10 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { // Jump to the next match. let direction = self.search_state.direction; - let origin = self.absolute_origin(); - match self.terminal.search_next(dfas, origin, direction, Side::Left, limit) { + let clamped_origin = self.search_state.origin.grid_clamp(self.terminal, Boundary::Grid); + match self.terminal.search_next(dfas, clamped_origin, direction, Side::Left, limit) { Some(regex_match) => { - let old_offset = self.terminal.grid().display_offset() as isize; + let old_offset = self.terminal.grid().display_offset() as i32; if self.terminal.mode().contains(TermMode::VI) { // Move vi cursor to the start of the match. @@ -789,7 +761,7 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { // Store number of lines the viewport had to be moved. let display_offset = self.terminal.grid().display_offset(); - self.search_state.display_offset_delta += old_offset - display_offset as isize; + self.search_state.display_offset_delta = old_offset - display_offset as i32; // Since we found a result, we require no delayed re-search. self.scheduler.unschedule(TimerId::DelayedSearch); @@ -817,14 +789,6 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { /// Cleanup the search state. fn exit_search(&mut self) { - // Move vi cursor down if resize will pull content from history. - if self.terminal.history_size() != 0 - && self.terminal.grid().display_offset() == 0 - && self.terminal.screen_lines() > self.terminal.vi_mode_cursor.point.line + 1 - { - self.terminal.vi_mode_cursor.point.line += 1; - } - self.display_update_pending.dirty = true; self.search_state.history_index = None; *self.dirty = true; @@ -833,20 +797,6 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { self.search_state.focused_match = None; } - /// Get the absolute position of the search origin. - /// - /// This takes the relative motion of the viewport since the start of the search into account. - /// So while the absolute point of the origin might have changed since new content was printed, - /// this will still return the correct absolute position. - fn absolute_origin(&self) -> Point<usize> { - let mut relative_origin = self.search_state.origin; - relative_origin.line = min(relative_origin.line, self.terminal.screen_lines() - 1); - relative_origin.column = min(relative_origin.column, self.terminal.cols() - 1); - let mut origin = self.terminal.visible_to_buffer(relative_origin); - origin.line = (origin.line as isize + self.search_state.display_offset_delta) as usize; - origin - } - /// Update the cursor blinking state. fn update_cursor_blinking(&mut self) { // Get config cursor style. @@ -886,8 +836,6 @@ pub enum ClickState { /// State of the mouse. #[derive(Debug)] pub struct Mouse { - pub x: usize, - pub y: usize, pub left_button_state: ElementState, pub middle_button_state: ElementState, pub right_button_state: ElementState, @@ -895,32 +843,32 @@ pub struct Mouse { pub last_click_button: MouseButton, pub click_state: ClickState, pub scroll_px: f64, - pub line: Line, - pub column: Column, pub cell_side: Side, pub lines_scrolled: f32, pub block_url_launcher: bool, pub inside_text_area: bool, + pub point: Point<usize>, + pub x: usize, + pub y: usize, } impl Default for Mouse { fn default() -> Mouse { Mouse { - x: 0, - y: 0, last_click_timestamp: Instant::now(), last_click_button: MouseButton::Left, left_button_state: ElementState::Released, middle_button_state: ElementState::Released, right_button_state: ElementState::Released, click_state: ClickState::None, - scroll_px: 0., - line: Line(0), - column: Column(0), cell_side: Side::Left, - lines_scrolled: 0., - block_url_launcher: false, - inside_text_area: false, + block_url_launcher: Default::default(), + inside_text_area: Default::default(), + lines_scrolled: Default::default(), + scroll_px: Default::default(), + point: Default::default(), + x: Default::default(), + y: Default::default(), } } } @@ -1409,14 +1357,12 @@ impl<N: Notify + OnResize> Processor<N> { { // Compute cursor positions before resize. let num_lines = terminal.screen_lines(); - let vi_mode = terminal.mode().contains(TermMode::VI); let cursor_at_bottom = terminal.grid().cursor.point.line + 1 == num_lines; - let origin_at_bottom = if vi_mode { + let origin_at_bottom = if terminal.mode().contains(TermMode::VI) { terminal.vi_mode_cursor.point.line == num_lines - 1 } else { self.search_state.direction == Direction::Left }; - let old_display_offset = terminal.grid().display_offset(); self.display.handle_update( terminal, @@ -1436,15 +1382,6 @@ impl<N: Notify + OnResize> Processor<N> { } else if display_offset != 0 && origin_at_bottom { terminal.scroll_display(Scroll::Delta(-1)); } - } else if old_is_searching - && !new_is_searching - && old_display_offset == 0 - && cursor_at_bottom - && origin_at_bottom - && vi_mode - { - // Pull down the vi cursor if it was moved up when the search was started. - terminal.vi_mode_cursor.point.line += 1; } } diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs index 0d6a066d..c5f41b6e 100644 --- a/alacritty/src/input.rs +++ b/alacritty/src/input.rs @@ -71,7 +71,6 @@ pub trait ActionContext<T: EventListener> { fn selection_is_empty(&self) -> bool; fn mouse_mut(&mut self) -> &mut Mouse; fn mouse(&self) -> &Mouse; - fn mouse_coords(&self) -> Option<Point>; fn received_count(&mut self) -> &mut usize; fn suppress_chars(&mut self) -> &mut bool; fn modifiers(&mut self) -> &mut ModifiersState; @@ -100,12 +99,7 @@ pub trait ActionContext<T: EventListener> { fn search_pop_word(&mut self) {} fn search_history_previous(&mut self) {} fn search_history_next(&mut self) {} - fn search_next( - &mut self, - origin: Point<usize>, - direction: Direction, - side: Side, - ) -> Option<Match>; + fn search_next(&mut self, origin: Point, direction: Direction, side: Side) -> Option<Match>; fn advance_search_origin(&mut self, _direction: Direction) {} fn search_direction(&self) -> Direction; fn search_active(&self) -> bool; @@ -121,8 +115,7 @@ impl Action { A: ActionContext<T>, T: EventListener, { - let cursor_point = ctx.terminal().vi_mode_cursor.point; - ctx.toggle_selection(ty, cursor_point, Side::Left); + ctx.toggle_selection(ty, ctx.terminal().vi_mode_cursor.point, Side::Left); // Make sure initial selection is not empty. if let Some(selection) = &mut ctx.terminal_mut().selection { @@ -171,17 +164,19 @@ impl<T: EventListener> Execute<T> for Action { }, Action::ViAction(ViAction::Open) => { ctx.mouse_mut().block_url_launcher = false; - if let Some(url) = ctx.urls().find_at(ctx.terminal().vi_mode_cursor.point) { + let vi_point = ctx.terminal().vi_mode_cursor.point; + let line = (vi_point.line + ctx.terminal().grid().display_offset()).0 as usize; + if let Some(url) = ctx.urls().find_at(Point::new(line, vi_point.column)) { ctx.launch_url(url); } }, Action::ViAction(ViAction::SearchNext) => { let terminal = ctx.terminal(); let direction = ctx.search_direction(); - let vi_point = terminal.visible_to_buffer(terminal.vi_mode_cursor.point); + let vi_point = terminal.vi_mode_cursor.point; let origin = match direction { - Direction::Right => vi_point.add_absolute(terminal, Boundary::Wrap, 1), - Direction::Left => vi_point.sub_absolute(terminal, Boundary::Wrap, 1), + Direction::Right => vi_point.add(terminal, Boundary::None, 1), + Direction::Left => vi_point.sub(terminal, Boundary::None, 1), }; if let Some(regex_match) = ctx.search_next(origin, direction, Side::Left) { @@ -192,10 +187,10 @@ impl<T: EventListener> Execute<T> for Action { Action::ViAction(ViAction::SearchPrevious) => { let terminal = ctx.terminal(); let direction = ctx.search_direction().opposite(); - let vi_point = terminal.visible_to_buffer(terminal.vi_mode_cursor.point); + let vi_point = terminal.vi_mode_cursor.point; let origin = match direction { - Direction::Right => vi_point.add_absolute(terminal, Boundary::Wrap, 1), - Direction::Left => vi_point.sub_absolute(terminal, Boundary::Wrap, 1), + Direction::Right => vi_point.add(terminal, Boundary::None, 1), + Direction::Left => vi_point.sub(terminal, Boundary::None, 1), }; if let Some(regex_match) = ctx.search_next(origin, direction, Side::Left) { @@ -205,9 +200,7 @@ impl<T: EventListener> Execute<T> for Action { }, Action::ViAction(ViAction::SearchStart) => { let terminal = ctx.terminal(); - let origin = terminal - .visible_to_buffer(terminal.vi_mode_cursor.point) - .sub_absolute(terminal, Boundary::Wrap, 1); + let origin = terminal.vi_mode_cursor.point.sub(terminal, Boundary::None, 1); if let Some(regex_match) = ctx.search_next(origin, Direction::Left, Side::Left) { ctx.terminal_mut().vi_goto_point(*regex_match.start()); @@ -216,9 +209,7 @@ impl<T: EventListener> Execute<T> for Action { }, Action::ViAction(ViAction::SearchEnd) => { let terminal = ctx.terminal(); - let origin = terminal - .visible_to_buffer(terminal.vi_mode_cursor.point) - .add_absolute(terminal, Boundary::Wrap, 1); + let origin = terminal.vi_mode_cursor.point.add(terminal, Boundary::None, 1); if let Some(regex_match) = ctx.search_next(origin, Direction::Right, Side::Right) { ctx.terminal_mut().vi_goto_point(*regex_match.end()); @@ -273,7 +264,7 @@ impl<T: EventListener> Execute<T> for Action { Action::ScrollPageUp => { // Move vi mode cursor. let term = ctx.terminal_mut(); - let scroll_lines = term.screen_lines().0 as isize; + let scroll_lines = term.screen_lines() as i32; term.vi_mode_cursor = term.vi_mode_cursor.scroll(term, scroll_lines); ctx.scroll(Scroll::PageUp); @@ -281,7 +272,7 @@ impl<T: EventListener> Execute<T> for Action { Action::ScrollPageDown => { // Move vi mode cursor. let term = ctx.terminal_mut(); - let scroll_lines = -(term.screen_lines().0 as isize); + let scroll_lines = -(term.screen_lines() as i32); term.vi_mode_cursor = term.vi_mode_cursor.scroll(term, scroll_lines); ctx.scroll(Scroll::PageDown); @@ -289,7 +280,7 @@ impl<T: EventListener> Execute<T> for Action { Action::ScrollHalfPageUp => { // Move vi mode cursor. let term = ctx.terminal_mut(); - let scroll_lines = term.screen_lines().0 as isize / 2; + let scroll_lines = term.screen_lines() as i32 / 2; term.vi_mode_cursor = term.vi_mode_cursor.scroll(term, scroll_lines); ctx.scroll(Scroll::Delta(scroll_lines)); @@ -297,37 +288,19 @@ impl<T: EventListener> Execute<T> for Action { Action::ScrollHalfPageDown => { // Move vi mode cursor. let term = ctx.terminal_mut(); - let scroll_lines = -(term.screen_lines().0 as isize / 2); + let scroll_lines = -(term.screen_lines() as i32 / 2); term.vi_mode_cursor = term.vi_mode_cursor.scroll(term, scroll_lines); ctx.scroll(Scroll::Delta(scroll_lines)); }, - Action::ScrollLineUp => { - // Move vi mode cursor. - let term = ctx.terminal(); - if term.grid().display_offset() != term.history_size() - && term.vi_mode_cursor.point.line + 1 != term.screen_lines() - { - ctx.terminal_mut().vi_mode_cursor.point.line += 1; - } - - ctx.scroll(Scroll::Delta(1)); - }, - Action::ScrollLineDown => { - // Move vi mode cursor. - if ctx.terminal().grid().display_offset() != 0 - && ctx.terminal().vi_mode_cursor.point.line.0 != 0 - { - ctx.terminal_mut().vi_mode_cursor.point.line -= 1; - } - - ctx.scroll(Scroll::Delta(-1)); - }, + Action::ScrollLineUp => ctx.scroll(Scroll::Delta(1)), + Action::ScrollLineDown => ctx.scroll(Scroll::Delta(-1)), Action::ScrollToTop => { ctx.scroll(Scroll::Top); // Move vi mode cursor. - ctx.terminal_mut().vi_mode_cursor.point.line = Line(0); + let topmost_line = ctx.terminal().topmost_line(); + ctx.terminal_mut().vi_mode_cursor.point.line = topmost_line; ctx.terminal_mut().vi_motion(ViMotion::FirstOccupied); ctx.mark_dirty(); }, @@ -336,7 +309,7 @@ impl<T: EventListener> Execute<T> for Action { // Move vi mode cursor. let term = ctx.terminal_mut(); - term.vi_mode_cursor.point.line = term.screen_lines() - 1; + term.vi_mode_cursor.point.line = term.bottommost_line(); // Move to beginning twice, to always jump across linewraps. term.vi_motion(ViMotion::FirstOccupied); @@ -414,11 +387,10 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { self.ctx.mouse_mut().y = y; let inside_text_area = size_info.contains_point(x, y); - let point = size_info.pixels_to_coords(x, y); - let cell_side = self.get_mouse_side(); + let point = self.coords_to_point(x, y); + let cell_side = self.cell_side(x); - let cell_changed = - point.line != self.ctx.mouse().line || point.column != self.ctx.mouse().column; + let cell_changed = point != self.ctx.mouse().point; // Update mouse state and check for URL change. let mouse_state = self.mouse_state(); @@ -435,14 +407,15 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { self.ctx.mouse_mut().inside_text_area = inside_text_area; self.ctx.mouse_mut().cell_side = cell_side; - self.ctx.mouse_mut().line = point.line; - self.ctx.mouse_mut().column = point.column; + self.ctx.mouse_mut().point = point; // Don't launch URLs if mouse has moved. self.ctx.mouse_mut().block_url_launcher = true; if (lmb_pressed || rmb_pressed) && (self.ctx.modifiers().shift() || !self.ctx.mouse_mode()) { + let line = Line(point.line as i32) - self.ctx.terminal().grid().display_offset(); + let point = Point::new(line, point.column); self.ctx.update_selection(point, cell_side); } else if cell_changed && point.line < self.ctx.terminal().screen_lines() @@ -460,9 +433,26 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { } } - fn get_mouse_side(&self) -> Side { + /// Convert window space pixels to terminal grid coordinates. + /// + /// If the coordinates are outside of the terminal grid, like positions inside the padding, the + /// coordinates will be clamped to the closest grid coordinates. + #[inline] + fn coords_to_point(&self, x: usize, y: usize) -> Point<usize> { + let size = self.ctx.size_info(); + + let column = x.saturating_sub(size.padding_x() as usize) / (size.cell_width() as usize); + let column = min(Column(column), size.last_column()); + + let line = y.saturating_sub(size.padding_y() as usize) / (size.cell_height() as usize); + let line = min(line, size.bottommost_line().0 as usize); + + Point::new(line, column) + } + + /// Check which side of a cell an X coordinate lies on. + fn cell_side(&self, x: usize) -> Side { let size_info = self.ctx.size_info(); - let x = self.ctx.mouse().x; let cell_x = x.saturating_sub(size_info.padding_x() as usize) % size_info.cell_width() as usize; @@ -483,12 +473,12 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { } fn normal_mouse_report(&mut self, button: u8) { - let (line, column) = (self.ctx.mouse().line, self.ctx.mouse().column); + let Point { line, column } = self.ctx.mouse().point; let utf8 = self.ctx.terminal().mode().contains(TermMode::UTF8_MOUSE); let max_point = if utf8 { 2015 } else { 223 }; - if line >= Line(max_point) || column >= Column(max_point) { + if line >= max_point || column >= Column(max_point) { return; } @@ -507,17 +497,17 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { msg.push(32 + 1 + column.0 as u8); } - if utf8 && line >= Line(95) { - msg.append(&mut mouse_pos_encode(line.0)); + if utf8 && line >= 95 { + msg.append(&mut mouse_pos_encode(line)); } else { - msg.push(32 + 1 + line.0 as u8); + msg.push(32 + 1 + line as u8); } self.ctx.write_to_pty(msg); } fn sgr_mouse_report(&mut self, button: u8, state: ElementState) { - let (line, column) = (self.ctx.mouse().line, self.ctx.mouse().column); + let Point { line, column } = self.ctx.mouse().point; let c = match state { ElementState::Pressed => 'M', ElementState::Released => 'm', @@ -589,9 +579,10 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { }; // Load mouse point, treating message bar and padding as the closest cell. - let mouse = self.ctx.mouse(); - let mut point = self.ctx.size_info().pixels_to_coords(mouse.x, mouse.y); - point.line = min(point.line, self.ctx.terminal().screen_lines() - 1); + let point = self.ctx.mouse().point; + let display_offset = self.ctx.terminal().grid().display_offset(); + let absolute_line = Line(point.line as i32) - display_offset; + let point = Point::new(absolute_line, point.column); match button { MouseButton::Left => self.on_left_click(point), @@ -751,24 +742,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { let lines = self.ctx.mouse().scroll_px / height; - // Store absolute position of vi mode cursor. - let term = self.ctx.terminal(); - let absolute = term.visible_to_buffer(term.vi_mode_cursor.point); - - self.ctx.scroll(Scroll::Delta(lines as isize)); - - // Try to restore vi mode cursor position, to keep it above its previous content. - let term = self.ctx.terminal_mut(); - term.vi_mode_cursor.point = term.grid().clamp_buffer_to_visible(absolute); - term.vi_mode_cursor.point.column = absolute.column; - - // Update selection. - if term.mode().contains(TermMode::VI) { - let point = term.vi_mode_cursor.point; - if !self.ctx.selection_is_empty() { - self.ctx.update_selection(point, Side::Right); - } - } + self.ctx.scroll(Scroll::Delta(lines as i32)); } self.ctx.mouse_mut().scroll_px %= height; @@ -977,13 +951,13 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { // Calculate Y position of the end of the last terminal line. let size = self.ctx.size_info(); let terminal_end = size.padding_y() as usize - + size.cell_height() as usize * (size.screen_lines().0 + search_height); + + size.cell_height() as usize * (size.screen_lines() + search_height); let mouse = self.ctx.mouse(); if self.ctx.message().is_none() || (mouse.y <= terminal_end) { None } else if mouse.y <= terminal_end + size.cell_height() as usize - && mouse.column + message_bar::CLOSE_BUTTON_TEXT.len() >= size.cols() + && mouse.point.column + message_bar::CLOSE_BUTTON_TEXT.len() >= size.columns() { Some(MouseState::MessageBarButton) } else { @@ -1055,7 +1029,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { // Compute the height of the scrolling areas. let end_top = max(min_height, size.padding_y() as i32); - let text_area_bottom = size.padding_y() + size.screen_lines().0 as f32 * size.cell_height(); + let text_area_bottom = size.padding_y() + size.screen_lines() as f32 * size.cell_height(); let start_bottom = min(size.height() as i32 - min_height, text_area_bottom as i32); // Get distance from closest window boundary. @@ -1069,7 +1043,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { }; // Scale number of lines scrolled based on distance to boundary. - let delta = delta as isize / step as isize; + let delta = delta as i32 / step as i32; let event = Event::Scroll(Scroll::Delta(delta)); // Schedule event. @@ -1120,7 +1094,7 @@ mod tests { impl<'a, T: EventListener> super::ActionContext<T> for ActionContext<'a, T> { fn search_next( &mut self, - _origin: Point<usize>, + _origin: Point, _direction: Direction, _side: Side, ) -> Option<Match> { @@ -1155,17 +1129,6 @@ mod tests { self.terminal.scroll_display(scroll); } - fn mouse_coords(&self) -> Option<Point> { - let x = self.mouse.x as usize; - let y = self.mouse.y as usize; - - if self.size_info.contains_point(x, y) { - Some(self.size_info.pixels_to_coords(x, y)) - } else { - None - } - } - fn mouse_mode(&self) -> bool { false } diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs index 30eff73d..0914aee4 100644 --- a/alacritty/src/main.rs +++ b/alacritty/src/main.rs @@ -25,6 +25,7 @@ use log::{error, info}; use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS}; use alacritty_terminal::event_loop::{self, EventLoop, Msg}; +use alacritty_terminal::grid::Dimensions; use alacritty_terminal::sync::FairMutex; use alacritty_terminal::term::Term; use alacritty_terminal::tty; @@ -139,7 +140,7 @@ fn run( info!( "PTY dimensions: {:?} x {:?}", display.size_info.screen_lines(), - display.size_info.cols() + display.size_info.columns() ); // Create the terminal. diff --git a/alacritty/src/message_bar.rs b/alacritty/src/message_bar.rs index 609af55d..72e6f354 100644 --- a/alacritty/src/message_bar.rs +++ b/alacritty/src/message_bar.rs @@ -1,8 +1,10 @@ use std::collections::VecDeque; -use alacritty_terminal::term::SizeInfo; use unicode_width::UnicodeWidthChar; +use alacritty_terminal::grid::Dimensions; +use alacritty_terminal::term::SizeInfo; + pub const CLOSE_BUTTON_TEXT: &str = "[X]"; const CLOSE_BUTTON_PADDING: usize = 1; const MIN_FREE_LINES: usize = 3; @@ -34,7 +36,7 @@ impl Message { /// Formatted message text lines. pub fn text(&self, size_info: &SizeInfo) -> Vec<String> { - let num_cols = size_info.cols().0; + let num_cols = size_info.columns(); let total_lines = (size_info.height() - 2. * size_info.padding_y()) / size_info.cell_height(); let max_lines = (total_lines as usize).saturating_sub(MIN_FREE_LINES); diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs index 4d752463..414d9bd5 100644 --- a/alacritty/src/renderer/mod.rs +++ b/alacritty/src/renderer/mod.rs @@ -481,7 +481,7 @@ impl Batch { self.instances.push(InstanceData { col: cell.point.column.0 as u16, - row: cell.point.line.0 as u16, + row: cell.point.line as u16, top: glyph.top, left: glyph.left, @@ -830,7 +830,7 @@ impl<'a> RenderApi<'a> { pub fn render_string( &mut self, glyph_cache: &mut GlyphCache, - point: Point, + point: Point<usize>, fg: Rgb, bg: Rgb, string: &str, @@ -846,7 +846,6 @@ impl<'a> RenderApi<'a> { bg_alpha: 1.0, fg, bg, - is_match: false, }) .collect::<Vec<_>>(); diff --git a/alacritty/src/renderer/rects.rs b/alacritty/src/renderer/rects.rs index fd966d27..591c9f46 100644 --- a/alacritty/src/renderer/rects.rs +++ b/alacritty/src/renderer/rects.rs @@ -3,6 +3,7 @@ use std::mem; use crossfont::Metrics; +use alacritty_terminal::grid::Dimensions; use alacritty_terminal::index::{Column, Point}; use alacritty_terminal::term::cell::Flags; use alacritty_terminal::term::color::Rgb; @@ -31,8 +32,8 @@ impl RenderRect { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct RenderLine { - pub start: Point, - pub end: Point, + pub start: Point<usize>, + pub end: Point<usize>, pub color: Rgb, } @@ -42,7 +43,7 @@ impl RenderLine { let mut start = self.start; while start.line < self.end.line { - let end = Point::new(start.line, size.cols() - 1); + let end = Point::new(start.line, size.last_column()); Self::push_rects(&mut rects, metrics, size, flag, start, end, self.color); start = Point::new(start.line + 1, Column(0)); } @@ -57,8 +58,8 @@ impl RenderLine { metrics: &Metrics, size: &SizeInfo, flag: Flags, - start: Point, - end: Point, + start: Point<usize>, + end: Point<usize>, color: Rgb, ) { let (position, thickness) = match flag { @@ -99,8 +100,8 @@ impl RenderLine { fn create_rect( size: &SizeInfo, descent: f32, - start: Point, - end: Point, + start: Point<usize>, + end: Point<usize>, position: f32, mut thickness: f32, color: Rgb, @@ -112,7 +113,7 @@ impl RenderLine { // Make sure lines are always visible. thickness = thickness.max(1.); - let line_bottom = (start.line.0 as f32 + 1.) * size.cell_height(); + let line_bottom = (start.line as f32 + 1.) * size.cell_height(); let baseline = line_bottom + descent; let mut y = (baseline - position - thickness / 2.).ceil(); diff --git a/alacritty/src/url.rs b/alacritty/src/url.rs index e4a29512..add4a1aa 100644 --- a/alacritty/src/url.rs +++ b/alacritty/src/url.rs @@ -5,7 +5,8 @@ use crossfont::Metrics; use glutin::event::{ElementState, ModifiersState}; use urlocator::{UrlLocation, UrlLocator}; -use alacritty_terminal::index::{Column, Point}; +use alacritty_terminal::grid::Dimensions; +use alacritty_terminal::index::{Boundary, Column, Line, Point}; use alacritty_terminal::term::cell::Flags; use alacritty_terminal::term::color::Rgb; use alacritty_terminal::term::SizeInfo; @@ -15,14 +16,15 @@ use crate::display::content::RenderableCell; use crate::event::Mouse; use crate::renderer::rects::{RenderLine, RenderRect}; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq)] pub struct Url { lines: Vec<RenderLine>, end_offset: u16, - num_cols: Column, + size: SizeInfo, } impl Url { + /// Rectangles required for underlining the URL. pub fn rects(&self, metrics: &Metrics, size: &SizeInfo) -> Vec<RenderRect> { let end = self.end(); self.lines @@ -37,20 +39,28 @@ impl Url { .collect() } - pub fn start(&self) -> Point { + /// Viewport start point of the URL. + pub fn start(&self) -> Point<usize> { self.lines[0].start } - pub fn end(&self) -> Point { - self.lines[self.lines.len() - 1].end.sub(self.num_cols, self.end_offset as usize) + /// Viewport end point of the URL. + pub fn end(&self) -> Point<usize> { + let end = self.lines[self.lines.len() - 1].end; + + // Convert to Point<Line> to make use of the grid clamping logic. + let mut end = Point::new(Line(end.line as i32), end.column); + end = end.sub(&self.size, Boundary::Cursor, self.end_offset as usize); + + Point::new(end.line.0 as usize, end.column) } } pub struct Urls { locator: UrlLocator, urls: Vec<Url>, - scheme_buffer: Vec<(Point, Rgb)>, - last_point: Option<Point>, + scheme_buffer: Vec<(Point<usize>, Rgb)>, + next_point: Point<usize>, state: UrlLocation, } @@ -61,7 +71,7 @@ impl Default for Urls { scheme_buffer: Vec::new(), urls: Vec::new(), state: UrlLocation::Reset, - last_point: None, + next_point: Point::new(0, Column(0)), } } } @@ -72,7 +82,7 @@ impl Urls { } // Update tracked URLs. - pub fn update(&mut self, num_cols: Column, cell: &RenderableCell) { + pub fn update(&mut self, size: &SizeInfo, cell: &RenderableCell) { let point = cell.point; let mut end = point; @@ -82,11 +92,15 @@ impl Urls { } // Reset URL when empty cells have been skipped. - if point != Point::default() && Some(point.sub(num_cols, 1)) != self.last_point { + if point != Point::new(0, Column(0)) && point != self.next_point { self.reset(); } - self.last_point = Some(end); + self.next_point = if end.column.0 + 1 == size.columns() { + Point::new(end.line + 1, Column(0)) + } else { + Point::new(end.line, end.column + 1) + }; // Extend current state if a leading wide char spacer is encountered. if cell.flags.intersects(Flags::LEADING_WIDE_CHAR_SPACER) { @@ -106,7 +120,7 @@ impl Urls { match (self.state, last_state) { (UrlLocation::Url(_length, end_offset), UrlLocation::Scheme) => { // Create empty URL. - self.urls.push(Url { lines: Vec::new(), end_offset, num_cols }); + self.urls.push(Url { lines: Vec::new(), end_offset, size: *size }); // Push schemes into URL. for (scheme_point, scheme_fg) in self.scheme_buffer.split_off(0) { @@ -125,13 +139,13 @@ impl Urls { } // Reset at un-wrapped linebreak. - if cell.point.column + 1 == num_cols && !cell.flags.contains(Flags::WRAPLINE) { + if cell.point.column.0 + 1 == size.columns() && !cell.flags.contains(Flags::WRAPLINE) { self.reset(); } } /// Extend the last URL. - fn extend_url(&mut self, start: Point, end: Point, color: Rgb, end_offset: u16) { + fn extend_url(&mut self, start: Point<usize>, end: Point<usize>, color: Rgb, end_offset: u16) { let url = self.urls.last_mut().unwrap(); // If color changed, we need to insert a new line. @@ -170,11 +184,11 @@ impl Urls { return None; } - self.find_at(Point::new(mouse.line, mouse.column)) + self.find_at(mouse.point) } /// Find URL at location. - pub fn find_at(&self, point: Point) -> Option<Url> { + pub fn find_at(&self, point: Point<usize>) -> Option<Url> { for url in &self.urls { if (url.start()..=url.end()).contains(&point) { return Some(url.clone()); @@ -194,7 +208,7 @@ impl Urls { mod tests { use super::*; - use alacritty_terminal::index::{Column, Line}; + use alacritty_terminal::index::Column; fn text_to_cells(text: &str) -> Vec<RenderableCell> { text.chars() @@ -202,12 +216,11 @@ mod tests { .map(|(i, character)| RenderableCell { character, zerowidth: None, - point: Point::new(Line(0), Column(i)), + point: Point::new(0, Column(i)), fg: Default::default(), bg: Default::default(), bg_alpha: 0., flags: Flags::empty(), - is_match: false, }) .collect() } @@ -215,14 +228,14 @@ mod tests { #[test] fn multi_color_url() { let mut input = text_to_cells("test https://example.org ing"); - let num_cols = input.len(); + let size = SizeInfo::new(input.len() as f32, 1., 1.0, 1.0, 0.0, 0.0, false); input[10].fg = Rgb { r: 0xff, g: 0x00, b: 0xff }; let mut urls = Urls::new(); for cell in input { - urls.update(Column(num_cols), &cell); + urls.update(&size, &cell); } let url = urls.urls.first().unwrap(); @@ -233,12 +246,12 @@ mod tests { #[test] fn multiple_urls() { let input = text_to_cells("test git:a git:b git:c ing"); - let num_cols = input.len(); + let size = SizeInfo::new(input.len() as f32, 1., 1.0, 1.0, 0.0, 0.0, false); let mut urls = Urls::new(); for cell in input { - urls.update(Column(num_cols), &cell); + urls.update(&size, &cell); } assert_eq!(urls.urls.len(), 3); @@ -256,12 +269,12 @@ mod tests { #[test] fn wide_urls() { let input = text_to_cells("test https://こんにちは (http:여보세요) ing"); - let num_cols = input.len() + 9; + let size = SizeInfo::new(input.len() as f32 + 9., 1., 1.0, 1.0, 0.0, 0.0, false); let mut urls = Urls::new(); for cell in input { - urls.update(Column(num_cols), &cell); + urls.update(&size, &cell); } assert_eq!(urls.urls.len(), 2); |