terminal: add FormatTag, text format tags
FormatTag describes a set of attributes, colors that exist in a tag_table field of CellBuffer. The field of tag_associations contains the hash of a tag and the {start,end} index of the cells that have this attribute. A tag can thus be used many times. An example of use is let t = self.pager.insert_tag(FormatTag { attrs: Attr::ITALICS, ..Default::default() }); debug!("FormatTag hash = {}", t); let (width, height) = self.pager.size(); for i in 0..height { if self.pager.content[(0, i)].ch() == '>' { self.pager.set_tag(t, (0, i), (width.saturating_sub(1), i)); } } This will set reply lines in text as italics. This feature interface is not used anywhere yet.async
parent
8c1c628c2c
commit
ef0f269fbf
|
@ -29,6 +29,8 @@ use crate::state::Context;
|
|||
use melib::text_processing::wcwidth;
|
||||
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use smallvec::SmallVec;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut, Index, IndexMut};
|
||||
|
@ -63,6 +65,8 @@ pub struct CellBuffer {
|
|||
pub ascii_drawing: bool,
|
||||
/// If printing to this buffer and we run out of space, expand it.
|
||||
growable: bool,
|
||||
tag_table: HashMap<u64, FormatTag>,
|
||||
tag_associations: SmallVec<[(u64, (usize, usize)); 128]>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for CellBuffer {
|
||||
|
@ -97,6 +101,8 @@ impl CellBuffer {
|
|||
buf: vec![cell; cols * rows],
|
||||
growable: false,
|
||||
ascii_drawing: false,
|
||||
tag_table: Default::default(),
|
||||
tag_associations: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,6 +113,8 @@ impl CellBuffer {
|
|||
buf: vec![cell; cols * rows],
|
||||
growable: false,
|
||||
ascii_drawing: context.settings.terminal.ascii_drawing,
|
||||
tag_table: Default::default(),
|
||||
tag_associations: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,6 +379,50 @@ impl CellBuffer {
|
|||
RowIterator { row, col: 0..0 }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tag_associations(&self) -> SmallVec<[(usize, u64, bool); 128]> {
|
||||
let mut ret: SmallVec<[(usize, u64, bool); 128]> = self.tag_associations.iter().fold(
|
||||
SmallVec::new(),
|
||||
|mut acc, (tag_hash, (start, end))| {
|
||||
acc.push((*start, *tag_hash, true));
|
||||
acc.push((*end, *tag_hash, false));
|
||||
acc
|
||||
},
|
||||
);
|
||||
ret.sort_by_key(|el| el.0);
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn tag_table(&self) -> &HashMap<u64, FormatTag> {
|
||||
&self.tag_table
|
||||
}
|
||||
|
||||
pub fn tag_table_mut(&mut self) -> &mut HashMap<u64, FormatTag> {
|
||||
&mut self.tag_table
|
||||
}
|
||||
|
||||
pub fn insert_tag(&mut self, tag: FormatTag) -> u64 {
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
tag.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
self.tag_table.insert(hash, tag);
|
||||
hash
|
||||
}
|
||||
|
||||
pub fn set_tag(&mut self, tag: u64, start: (usize, usize), end: (usize, usize)) {
|
||||
let start = self
|
||||
.pos_to_index(start.0, start.1)
|
||||
.unwrap_or(self.buf.len().saturating_sub(1));
|
||||
let end = self
|
||||
.pos_to_index(end.0, end.1)
|
||||
.unwrap_or(self.buf.len().saturating_sub(1));
|
||||
if start != end {
|
||||
self.tag_associations.push((tag, (start, end)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for CellBuffer {
|
||||
|
@ -662,7 +714,7 @@ impl Default for Cell {
|
|||
/// // Basic colors are also 8-bit colors (but not vice-versa).
|
||||
/// assert_eq!(red.as_byte(), fancy.as_byte())
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Hash, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Color {
|
||||
Black,
|
||||
Red,
|
||||
|
@ -1647,9 +1699,27 @@ pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area,
|
|||
return upper_left!(dest);
|
||||
}
|
||||
|
||||
let tag_associations = grid_src.tag_associations();
|
||||
let start_idx = grid_src.pos_to_index(src_x, src_y).unwrap();
|
||||
let mut tag_offset: usize = tag_associations
|
||||
.binary_search_by(|probe| probe.0.cmp(&start_idx))
|
||||
.unwrap_or_else(|i| i);
|
||||
let mut stack: HashSet<u64> = HashSet::default();
|
||||
for y in get_y(upper_left!(dest))..=get_y(bottom_right!(dest)) {
|
||||
'for_x: for x in get_x(upper_left!(dest))..=get_x(bottom_right!(dest)) {
|
||||
let idx = grid_src.pos_to_index(src_x, src_y).unwrap();
|
||||
while tag_offset < tag_associations.len() && tag_associations[tag_offset].0 <= idx {
|
||||
if tag_associations[tag_offset].2 {
|
||||
stack.insert(tag_associations[tag_offset].1);
|
||||
} else {
|
||||
stack.remove(&tag_associations[tag_offset].1);
|
||||
}
|
||||
tag_offset += 1;
|
||||
}
|
||||
grid_dest[(x, y)] = grid_src[(src_x, src_y)];
|
||||
for t in &stack {
|
||||
grid_dest[(x, y)].attrs |= grid_src.tag_table()[&t].attrs;
|
||||
}
|
||||
if src_x >= get_x(bottom_right!(src)) {
|
||||
break 'for_x;
|
||||
}
|
||||
|
@ -2204,6 +2274,8 @@ pub mod ansi {
|
|||
cols: max_cols,
|
||||
growable: false,
|
||||
ascii_drawing: false,
|
||||
tag_table: Default::default(),
|
||||
tag_associations: smallvec::SmallVec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -2717,3 +2789,11 @@ fn test_cellbuffer_search() {
|
|||
println!("");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Hash, Clone, PartialEq, Eq)]
|
||||
pub struct FormatTag {
|
||||
pub fg: Color,
|
||||
pub bg: Color,
|
||||
pub attrs: Attr,
|
||||
pub priority: u8,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue