Skip to content

Commit

Permalink
Small improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
fee1-dead committed Dec 21, 2021
1 parent 6cd8751 commit 89e7102
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 93 deletions.
9 changes: 7 additions & 2 deletions alacritty/src/display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,13 @@ impl Display {
let background_color = content.color(NamedColor::Background as usize);
let display_offset = content.display_offset();
let grid_text_runs: Vec<TextRun> = {
TextRunIter::<()>::from_content(&mut content, highlighted_hint, vi_highlighted_hint)
.collect()
let mut vec = Vec::with_capacity(terminal.screen_lines() * 2);
vec.extend(TextRunIter::from_content(
&mut content,
highlighted_hint,
vi_highlighted_hint,
));
vec
};
let cursor = content.cursor();

Expand Down
9 changes: 5 additions & 4 deletions alacritty/src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -954,21 +954,22 @@ impl<'a> RenderApi<'a> {
}

pub fn render_text_run(&mut self, text_run: TextRun, glyph_cache: &mut GlyphCache) {
let TextRunContent { text, zero_widths } = &text_run.content;

// Get font key for cell
let font_key = Self::determine_font_key(text_run.flags, glyph_cache);

let shaped_glyphs = if text_run.flags.contains(Flags::HIDDEN) {
GlyphIter::Hidden
} else {
GlyphIter::Shaped(
glyph_cache.shape_run(text, font_key, self).expect("read font").into_iter(),
glyph_cache
.shape_run(&text_run.content.text, font_key, self)
.expect("read font")
.into_iter(),
)
};

for ((mut cell, glyph), zero_width_chars) in
text_run.cells().zip(shaped_glyphs).zip(zero_widths.iter())
text_run.cells().zip(shaped_glyphs).zip(text_run.content.zero_widths.iter())
{
self.add_render_item(&cell, &glyph);
// Add empty spacer for full width characters
Expand Down
185 changes: 99 additions & 86 deletions alacritty/src/text_run.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use std::mem::replace;

use alacritty_terminal::index::{Column, Line, Point};
use alacritty_terminal::term::cell::Flags;
use alacritty_terminal::term::color::Rgb;
use alacritty_terminal::term::search::Match;

use crate::display::content::{RenderableCell, RenderableContent};

#[derive(Debug)]
#[derive(Debug, Default, Clone, Copy)]
struct RunStart {
line: usize,
column: Column,
Expand All @@ -16,14 +18,25 @@ struct RunStart {
}

impl RunStart {
fn new(cell: &RenderableCell) -> Self {
Self {
line: cell.point.line,
column: cell.point.column,
fg: cell.fg,
bg: cell.bg,
bg_alpha: cell.bg_alpha,
flags: cell.flags,
}
}

/// Compare cell and check if it belongs to the same run.
#[inline]
fn belongs_to_text_run(&self, render_cell: &RenderableCell) -> bool {
self.line == render_cell.point.line
&& self.fg == render_cell.fg
&& self.bg == render_cell.bg
&& (self.bg_alpha - render_cell.bg_alpha).abs() < std::f32::EPSILON
&& self.flags == render_cell.flags
&& self.bg == render_cell.bg
&& (self.bg_alpha - render_cell.bg_alpha).abs() < f32::EPSILON
}
}

Expand Down Expand Up @@ -80,7 +93,7 @@ impl TextRun {
Point { line: self.line, column: self.span.1 }
}

/// Iterates over each RenderableCell in column range [run.0, run.1]
/// Iterates over each RenderableCell in column range `[run.0, run.1]`
pub fn cells(&self) -> impl Iterator<Item = RenderableCell> + '_ {
let step = if self.flags.contains(Flags::WIDE_CHAR) { 2 } else { 1 };
let (Column(start), Column(end)) = self.span;
Expand All @@ -89,14 +102,24 @@ impl TextRun {
}
}

type IsWide = bool;
type LatestCol = (Column, IsWide);
#[derive(Default, Clone, Copy)]
pub struct LatestCol {
column: Column,
is_wide: bool,
}

impl LatestCol {
#[inline]
fn new(cell: &RenderableCell) -> Self {
Self { column: cell.point.column, is_wide: cell.flags.contains(Flags::WIDE_CHAR) }
}
}

/// Wraps an Iterator<Item=RenderableCell> and produces TextRuns to represent batches of cells
pub struct TextRunIter<I> {
iter: I,
run_start: Option<RunStart>,
latest_col: Option<LatestCol>,
run_start: RunStart,
latest_col: LatestCol,
display_offset: usize,
hint: Option<Match>,
vi_hint: Option<Match>,
Expand All @@ -107,12 +130,12 @@ pub struct TextRunIter<I> {
type TextRunIterFromContent<'a, 'c> =
TextRunIter<std::iter::Filter<&'a mut RenderableContent<'c>, fn(&RenderableCell) -> bool>>;

impl<I> TextRunIter<I> {
pub fn from_content<'a, 'c>(
impl<'a, 'c> TextRunIterFromContent<'a, 'c> {
pub fn from_content(
content: &'a mut RenderableContent<'c>,
hint: Option<Match>,
vi_hint: Option<Match>,
) -> TextRunIterFromContent<'a, 'c> {
) -> Self {
fn check(cell: &RenderableCell) -> bool {
!cell.flags.contains(Flags::WIDE_CHAR_SPACER)
}
Expand All @@ -130,41 +153,60 @@ where
I: Iterator<Item = RenderableCell>,
{
pub fn new(
iter: I,
mut iter: I,
hint: Option<Match>,
vi_hint: Option<Match>,
display_offset: usize,
) -> Self {
TextRunIter {
iter,
latest_col: None,
display_offset,
run_start: None,
buffer_text: String::new(),
buffer_zero_width: Vec::new(),
hint,
vi_hint,
if let Some(cell) = iter.next() {
let latest_col = LatestCol::new(&cell);
let run_start = RunStart::new(&cell);
let buffer_text = cell.character.to_string();
let buffer_zero_width = vec![cell.zerowidth];

TextRunIter {
iter,
latest_col,
display_offset,
run_start,
buffer_text,
buffer_zero_width,
hint,
vi_hint,
}
} else {
// There are no cells in the grid. This rarely happens.
#[cold]
#[inline]
fn dummy<I>(iter: I) -> TextRunIter<I> {
TextRunIter {
iter,
latest_col: LatestCol::default(),
display_offset: 0,
run_start: RunStart::default(),
buffer_text: String::new(),
buffer_zero_width: Vec::new(),
hint: None,
vi_hint: None,
}
}

dummy(iter)
}
}
}
impl<I> TextRunIter<I> {
/// Check if the cell belongs to this text run. Returns `true` if it does not belong.
fn cell_does_not_belong_to_run(&self, render_cell: &RenderableCell) -> bool {
self.run_start
.as_ref()
.map(|run_start| !run_start.belongs_to_text_run(render_cell))
.unwrap_or_default()
!self.run_start.belongs_to_text_run(render_cell)
}

/// Check if the column is not adjacent to the latest column.
fn is_col_not_adjacent(&self, column: Column) -> bool {
self.latest_col
.as_ref()
.map(|&(col, is_wide)| {
let width = if is_wide { 2 } else { 1 };
col + width != column && column + width != col
})
.unwrap_or_default()
fn is_col_not_adjacent(&self, col: Column) -> bool {
let LatestCol { column, is_wide } = self.latest_col;

let width = if is_wide { 2 } else { 1 };
col + width != column && column + width != col
}

/// Check if current run ends at incoming RenderableCell
Expand All @@ -184,7 +226,8 @@ impl<I> TextRunIter<I> {
/// Empty out pending buffer producing owned collections that can be moved into a TextRun
fn drain_buffer(&mut self) -> TextRunContent {
use std::mem::take;
let text = take(&mut self.buffer_text);
let text = self.buffer_text.clone();
self.buffer_text.clear();
let zero_widths = take(&mut self.buffer_zero_width);

TextRunContent { text, zero_widths }
Expand All @@ -204,40 +247,28 @@ impl<I> TextRunIter<I> {

/// Start a new run by setting latest_col, run_start, and buffering content of rc
/// Returns the previous runs run_start and latest_col data if available.
fn start_run(&mut self, render_cell: RenderableCell) -> (Option<RunStart>, Option<LatestCol>) {
let latest = self
.latest_col
.replace((render_cell.point.column, render_cell.flags.contains(Flags::WIDE_CHAR)));
let start = self.run_start.replace(RunStart {
line: render_cell.point.line,
column: render_cell.point.column,
fg: render_cell.fg,
bg: render_cell.bg,
bg_alpha: render_cell.bg_alpha,
flags: render_cell.flags,
});
fn start_run(&mut self, render_cell: RenderableCell) -> (RunStart, LatestCol) {
let prev_start = replace(&mut self.run_start, RunStart::new(&render_cell));
let prev_latest = replace(&mut self.latest_col, LatestCol::new(&render_cell));

self.buffer_content(render_cell);
(start, latest)

(prev_start, prev_latest)
}

/// Create a run of chars from the current state of the `TextRunIter`.
/// This is a destructive operation, the iterator will be in a new run state after it's
/// completion.
fn produce_char_run(&mut self, render_cell: RenderableCell) -> Option<TextRun> {
fn produce_char_run(&mut self, render_cell: RenderableCell) -> TextRun {
let prev_buffer = self.drain_buffer();
let (start_opt, latest_col_opt) = self.start_run(render_cell);
let start = start_opt?;
let latest_col = latest_col_opt?;
Some(Self::build_text_run(start, latest_col, prev_buffer))
let (start, latest_col) = self.start_run(render_cell);

Self::build_text_run(start, latest_col, prev_buffer)
}

/// Build a TextRun instance from passed state of TextRunIter
fn build_text_run(
start: RunStart,
(latest, is_wide): LatestCol,
content: TextRunContent,
) -> TextRun {
let end_column = if is_wide { latest + 1 } else { latest };
fn build_text_run(start: RunStart, latest_col: LatestCol, content: TextRunContent) -> TextRun {
let end_column = latest_col.column + latest_col.is_wide as usize;
TextRun {
line: start.line,
span: (start.column, end_column),
Expand All @@ -257,46 +288,28 @@ where
type Item = TextRun;

fn next(&mut self) -> Option<Self::Item> {
let mut output = None;
while let Some(mut render_cell) = self.iter.next() {
if self.is_hinted(render_cell.point) {
render_cell.flags.insert(Flags::UNDERLINE);
}
if self.latest_col.is_none() || self.run_start.is_none() {
// Initial state, this is should only be hit on the first next() call of
// iterator

self.run_start = Some(RunStart {
line: render_cell.point.line,
column: render_cell.point.column,
fg: render_cell.fg,
bg: render_cell.bg,
bg_alpha: render_cell.bg_alpha,
flags: render_cell.flags,
});
} else if self.is_end_of_run(&render_cell) {
if self.is_end_of_run(&render_cell) {
// If we find a run break,
// return what we have so far and start a new run.
output = self.produce_char_run(render_cell);
break;
return Some(self.produce_char_run(render_cell));
}

// Build up buffer and track the latest column we've seen
self.latest_col =
Some((render_cell.point.column, render_cell.flags.contains(Flags::WIDE_CHAR)));
self.latest_col = LatestCol::new(&render_cell);
self.buffer_content(render_cell);
}

// Check for any remaining buffered content and return it as a text run.
// This is a destructive operation, it will return None after it excutes once.
output.or_else(|| {
if !self.buffer_text.is_empty() || !self.buffer_zero_width.is_empty() {
let start = self.run_start.take()?;
let latest_col = self.latest_col.take()?;
// Save leftover buffer and empty it
Some(Self::build_text_run(start, latest_col, self.drain_buffer()))
} else {
None
}
})
if !self.buffer_text.is_empty() || !self.buffer_zero_width.is_empty() {
// Save leftover buffer and empty it
Some(Self::build_text_run(self.run_start, self.latest_col, self.drain_buffer()))
} else {
None
}
}
}
2 changes: 1 addition & 1 deletion alacritty_terminal/src/term/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::grid::{self, GridCell};
use crate::index::Column;

bitflags! {
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Default)]
pub struct Flags: u16 {
const INVERSE = 0b0000_0000_0000_0001;
const BOLD = 0b0000_0000_0000_0010;
Expand Down

0 comments on commit 89e7102

Please sign in to comment.