use core::iter::Iterator;
use super::*;
use crate::error_consts::*;
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
struct Line {
tag: char,
matched: bool,
text: String,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct VecBuffer {
saved: bool,
buffer: Vec<Line>,
clipboard: Vec<Line>,
}
impl VecBuffer {
pub fn new() -> Self
{
Self{
saved: true,
buffer: Vec::new(),
clipboard: Vec::new(),
}
}
}
impl Buffer for VecBuffer {
fn len(&self) -> usize {
self.buffer.len()
}
fn get_tag(&self, tag: char)
-> Result<usize, &'static str>
{
let mut index = 0;
for line in &self.buffer[..] {
if &tag == &line.tag { return Ok(index); }
index += 1;
}
Err(NO_MATCH)
}
fn get_matching(&self, pattern: &str, curr_line: usize, backwards: bool)
-> Result<usize, &'static str>
{
verify_index(self, curr_line)?;
use regex::RegexBuilder;
let regex = RegexBuilder::new(pattern)
.multi_line(true)
.build()
.map_err(|_| INVALID_REGEX)
?;
let length = if ! backwards {
self.buffer.len().saturating_sub(curr_line + 1)
} else {
curr_line
};
for index in 0 .. length {
if backwards {
if regex.is_match(&(self.buffer[curr_line - 1 - index].text)) {
return Ok(curr_line - 1 - index)
}
} else {
if regex.is_match(&(self.buffer[curr_line + index + 1].text)) {
return Ok(curr_line + index + 1)
}
}
}
Err(NO_MATCH)
}
fn mark_matching(&mut self, pattern: &str, selection: (usize, usize), inverse: bool)
-> Result<(), &'static str>
{
use regex::RegexBuilder;
verify_selection(self, selection)?;
let regex = RegexBuilder::new(pattern)
.multi_line(true)
.build()
.map_err(|_| INVALID_REGEX)
?;
for index in 0 .. self.len() {
if index >= selection.0 && index <= selection.1 {
self.buffer[index].matched = regex.is_match(&(self.buffer[index].text)) ^ inverse;
}
else {
self.buffer[index].matched = false;
}
}
Ok(())
}
fn get_marked(&mut self)
-> Result<Option<usize>, &'static str>
{
for index in 0 .. self.buffer.len() {
if self.buffer[index].matched {
self.buffer[index].matched = false;
return Ok(Some(index));
}
}
Ok(None)
}
fn tag_line(&mut self, index: usize, tag: char)
-> Result<(), &'static str>
{
self.buffer[index].tag = tag;
Ok(())
}
fn insert<'a>(&mut self, data: &mut dyn Iterator<Item = &'a str>, index: usize)
-> Result<(), &'static str>
{
verify_index(self, index)?;
self.saved = false;
let mut tail = self.buffer.split_off(index);
for line in data {
self.buffer.push(Line{tag: '\0', matched: false, text: line.to_string()});
}
self.buffer.append(&mut tail);
Ok(())
}
fn cut(&mut self, selection: (usize, usize)) -> Result<(), &'static str>
{
verify_selection(self, selection)?;
self.saved = false;
let mut tail = self.buffer.split_off(selection.1 + 1);
self.clipboard = self.buffer.split_off(selection.0);
self.buffer.append(&mut tail);
Ok(())
}
fn change<'a>(&mut self, data: &mut dyn Iterator<Item = &'a str>, selection: (usize, usize))
-> Result<(), &'static str>
{
verify_selection(self, selection)?;
self.saved = false;
let mut tail = self.buffer.split_off(selection.1 + 1);
self.clipboard = self.buffer.split_off(selection.0);
for line in data {
self.buffer.push(Line{tag: '\0', matched: false, text: line.to_string()});
}
self.buffer.append(&mut tail);
Ok(())
}
fn mov(&mut self, selection: (usize, usize), index: usize) -> Result<(), &'static str> {
verify_selection(self, selection)?;
verify_index(self, index)?;
if index <= selection.0 {
let mut tail = self.buffer.split_off(selection.1 + 1);
let mut data = self.buffer.split_off(selection.0);
let mut middle = self.buffer.split_off(index.saturating_sub(1));
self.buffer.append(&mut data);
self.buffer.append(&mut middle);
self.buffer.append(&mut tail);
Ok(())
}
else if index >= selection.1 {
let mut tail = self.buffer.split_off(index);
let mut middle = self.buffer.split_off(selection.1 + 1);
let mut data = self.buffer.split_off(selection.0);
self.buffer.append(&mut middle);
self.buffer.append(&mut data);
self.buffer.append(&mut tail);
Ok(())
}
else {
Err(MOVE_INTO_SELF)
}
}
fn mov_copy(&mut self, selection: (usize, usize), index: usize) -> Result<(), &'static str> {
verify_selection(self, selection)?;
verify_index(self, index)?;
let mut data = Vec::new();
for line in &self.buffer[selection.0 ..= selection.1] {
data.push(line.clone());
}
let i = if index <= selection.0 {
index.saturating_sub(1)
}
else {
index
};
let mut tail = self.buffer.split_off(i);
self.buffer.append(&mut data);
self.buffer.append(&mut tail);
Ok(())
}
fn join(&mut self, selection: (usize, usize)) -> Result<(), &'static str> {
verify_selection(self, selection)?;
let mut tail = self.buffer.split_off(selection.1 + 1);
let data = self.buffer.split_off(selection.0 + 1);
self.buffer.append(&mut tail);
for line in data {
self.buffer[selection.0].text.pop(); self.buffer[selection.0].text.push_str(&line.text); }
Ok(())
}
fn copy(&mut self, selection: (usize, usize)) -> Result<(), &'static str> {
verify_selection(self, selection)?;
self.clipboard = Vec::new();
for line in &self.buffer[selection.0 ..= selection.1] {
self.clipboard.push(line.clone());
}
Ok(())
}
fn paste(&mut self, index: usize) -> Result<usize, &'static str> {
verify_index(self, index)?;
let mut tmp = self.buffer.split_off(index);
for line in &self.clipboard {
self.buffer.push(line.clone());
}
self.buffer.append(&mut tmp);
Ok(self.clipboard.len())
}
fn search_replace(&mut self, pattern: (&str, &str), selection: (usize, usize), global: bool) -> Result<(usize, usize), &'static str>
{
use regex::RegexBuilder;
verify_selection(self, selection)?;
self.saved = false; let regex = RegexBuilder::new(pattern.0)
.multi_line(true)
.build()
.map_err(|_| INVALID_REGEX)
?;
let mut selection_after = selection;
let mut tail = self.buffer.split_off(selection.1 + 1);
let before = self.buffer.split_off(selection.0 + 1);
let mut tmp = self.buffer.pop().unwrap();
for line in before {
tmp.text.push_str(&line.text);
}
let mut after = if global {
regex.replace_all(&tmp.text, pattern.1).to_string()
}
else {
regex.replace(&tmp.text, pattern.1).to_string()
};
if !after.ends_with('\n') {
if tail.len() > 0 {
after.push_str(&tail.remove(0).text);
}
else {
after.push('\n');
}
}
for line in after.lines() {
self.buffer.push(Line{tag: '\0', matched: false, text: format!("{}\n", line)});
}
selection_after.1 = self.buffer.len();
self.buffer.append(&mut tail);
Ok(selection_after)
}
fn read_from(&mut self, path: &str, index: Option<usize>, must_exist: bool)
-> Result<usize, &'static str>
{
if let Some(i) = index { verify_index(self, i)?; }
let data = file::read_file(path, must_exist)?;
let len = data.len();
let mut iter = data.iter().map(| string | &string[..]);
let i = match index {
Some(i) => i,
None => {
self.buffer.clear();
0
},
};
self.insert(&mut iter, i)?;
Ok(len)
}
fn write_to(&mut self, selection: Option<(usize, usize)>, path: &str, append: bool)
-> Result<(), &'static str>
{
let data = match selection {
Some(sel) => self.get_selection(sel)?,
None => Box::new(self.buffer[..].iter().map(|line| &line.text[..])),
};
file::write_file(path, data, append)?;
if selection == Some((0, self.len().saturating_sub(1))) || selection.is_none() {
self.saved = true;
}
Ok(())
}
fn saved(&self) -> bool {
self.saved
}
fn get_selection<'a>(&'a self, selection: (usize, usize))
-> Result<Box<dyn Iterator<Item = &'a str> + 'a>, &'static str>
{
verify_selection(self, selection)?;
let tmp = self.buffer[selection.0 ..= selection.1].iter().map(|line| &line.text[..]);
Ok(Box::new(tmp))
}
}