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 melib::text_processing::wcwidth;
|
||||||
|
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Deref, DerefMut, Index, IndexMut};
|
use std::ops::{Deref, DerefMut, Index, IndexMut};
|
||||||
|
@ -63,6 +65,8 @@ pub struct CellBuffer {
|
||||||
pub ascii_drawing: bool,
|
pub ascii_drawing: bool,
|
||||||
/// If printing to this buffer and we run out of space, expand it.
|
/// If printing to this buffer and we run out of space, expand it.
|
||||||
growable: bool,
|
growable: bool,
|
||||||
|
tag_table: HashMap<u64, FormatTag>,
|
||||||
|
tag_associations: SmallVec<[(u64, (usize, usize)); 128]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for CellBuffer {
|
impl fmt::Debug for CellBuffer {
|
||||||
|
@ -97,6 +101,8 @@ impl CellBuffer {
|
||||||
buf: vec![cell; cols * rows],
|
buf: vec![cell; cols * rows],
|
||||||
growable: false,
|
growable: false,
|
||||||
ascii_drawing: false,
|
ascii_drawing: false,
|
||||||
|
tag_table: Default::default(),
|
||||||
|
tag_associations: SmallVec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +113,8 @@ impl CellBuffer {
|
||||||
buf: vec![cell; cols * rows],
|
buf: vec![cell; cols * rows],
|
||||||
growable: false,
|
growable: false,
|
||||||
ascii_drawing: context.settings.terminal.ascii_drawing,
|
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 }
|
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 {
|
impl Deref for CellBuffer {
|
||||||
|
@ -662,7 +714,7 @@ impl Default for Cell {
|
||||||
/// // Basic colors are also 8-bit colors (but not vice-versa).
|
/// // Basic colors are also 8-bit colors (but not vice-versa).
|
||||||
/// assert_eq!(red.as_byte(), fancy.as_byte())
|
/// assert_eq!(red.as_byte(), fancy.as_byte())
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Hash, Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
Black,
|
Black,
|
||||||
Red,
|
Red,
|
||||||
|
@ -1647,9 +1699,27 @@ pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area,
|
||||||
return upper_left!(dest);
|
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 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)) {
|
'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)];
|
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)) {
|
if src_x >= get_x(bottom_right!(src)) {
|
||||||
break 'for_x;
|
break 'for_x;
|
||||||
}
|
}
|
||||||
|
@ -2204,6 +2274,8 @@ pub mod ansi {
|
||||||
cols: max_cols,
|
cols: max_cols,
|
||||||
growable: false,
|
growable: false,
|
||||||
ascii_drawing: false,
|
ascii_drawing: false,
|
||||||
|
tag_table: Default::default(),
|
||||||
|
tag_associations: smallvec::SmallVec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2717,3 +2789,11 @@ fn test_cellbuffer_search() {
|
||||||
println!("");
|
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