use std::collections::HashMap;
use crate::utils::{Loc, width};
use unicode_width::UnicodeWidthChar;
type CharHashMap = HashMap<usize, Vec<(usize, usize)>>;
#[derive(Default, Clone, PartialEq, Eq, Debug)]
pub struct CharMap {
pub map: CharHashMap,
}
impl CharMap {
#[must_use]
pub fn new(map: CharHashMap) -> Self {
Self { map }
}
pub fn add(&mut self, idx: usize, val: (usize, usize)) {
if let Some(map) = self.map.get_mut(&idx) {
map.push(val);
} else {
self.map.insert(idx, vec![val]);
}
}
pub fn insert(&mut self, idx: usize, slice: Vec<(usize, usize)>) {
if !slice.is_empty() {
self.map.insert(idx, slice);
}
}
pub fn delete(&mut self, idx: usize) {
self.map.remove(&idx);
}
#[must_use]
pub fn get(&self, idx: usize) -> Option<&Vec<(usize, usize)>> {
self.map.get(&idx)
}
#[must_use]
pub fn contains(&self, idx: usize) -> bool {
self.map.contains_key(&idx)
}
pub fn splice(&mut self, loc: &Loc, start: usize, slice: Vec<(usize, usize)>) {
if let Some(map) = self.map.get_mut(&loc.y) {
map.splice(start..start, slice);
} else if !slice.is_empty() {
self.map.insert(loc.y, slice);
}
}
#[allow(clippy::missing_panics_doc)]
pub fn shift_insertion(&mut self, loc: &Loc, st: &str, tab_width: usize) -> usize {
if !self.map.contains_key(&loc.y) { return 0; }
let char_shift = st.chars().count();
let disp_shift = width(st, tab_width);
let start = self.count(loc, false).unwrap();
let line_map = self.map.get_mut(&loc.y).unwrap();
for (display, ch) in line_map.iter_mut().skip(start) {
*display += disp_shift;
*ch += char_shift;
}
start
}
#[allow(clippy::missing_panics_doc)]
pub fn shift_deletion(&mut self, loc: &Loc, x: (usize, usize), st: &str, tab_width: usize) {
if !self.map.contains_key(&loc.y) { return; }
let char_shift = st.chars().count();
let disp_shift = width(st, tab_width);
let (start, end) = x;
let Loc { x: line_start, y } = loc;
let start_map = self.count(&Loc { x: start - line_start, y: *y}, false).unwrap();
let map_count = self.count(&Loc { x: end - line_start, y: *y}, false).unwrap();
let line_map = self.map.get_mut(y).unwrap();
for (display, ch) in line_map.iter_mut().skip(map_count) {
*display -= disp_shift;
*ch -= char_shift;
}
line_map.drain(start_map..map_count);
if line_map.is_empty() {
self.map.remove(y);
}
}
#[allow(clippy::missing_panics_doc)]
pub fn shift_up(&mut self, loc: usize) {
let mut keys: Vec<usize> = self.map.keys().copied().collect();
keys.sort_unstable();
for k in keys {
if k >= loc {
let v = self.map.remove(&k).unwrap();
self.map.insert(k - 1, v);
}
}
}
#[allow(clippy::missing_panics_doc)]
pub fn shift_down(&mut self, loc: usize) {
let mut keys: Vec<usize> = self.map.keys().copied().collect();
keys.sort_unstable();
keys.reverse();
for k in keys {
if k >= loc {
let v = self.map.remove(&k).unwrap();
self.map.insert(k + 1, v);
}
}
}
#[must_use]
pub fn count(&self, loc: &Loc, display: bool) -> Option<usize> {
let mut ctr = 0;
for i in self.get(loc.y)? {
let i = if display { i.0 } else { i.1 };
if i >= loc.x {
break;
}
ctr += 1;
}
Some(ctr)
}
}
pub type DblUsize = Vec<(usize, usize)>;
#[must_use]
pub fn form_map(st: &str, tab_width: usize) -> (DblUsize, DblUsize) {
let mut dbl = vec![];
let mut tab = vec![];
let mut idx = 0;
for (char_idx, ch) in st.chars().enumerate() {
if ch == '\t' {
tab.push((idx, char_idx));
idx += tab_width;
} else if ch.width().unwrap_or(1) == 1 {
idx += 1;
} else {
dbl.push((idx, char_idx));
idx += 2;
}
}
(dbl, tab)
}