use crate::error::*;
use crate::Ed;
#[derive(PartialEq, Debug)]
pub enum Ind <'a> {
Selection,
BufferLen,
Literal(usize),
Tag(char),
Pattern(&'a str),
RevPattern(&'a str),
Add(Box<Ind<'a>>, usize),
Sub(Box<Ind<'a>>, usize),
}
pub enum Sel <'a> {
Pair(Ind<'a>, Ind<'a>),
Lone(Ind<'a>)
}
enum State {
Default(usize),
Tag,
Pattern(usize),
RevPattern(usize),
Offset(usize, bool),
}
pub fn parse_index(
input: &str,
) -> Result<(usize, Option<Ind<'_>>)> {
let mut i;
let mut ch;
let mut state = State::Default(0);
let mut current_ind = None;
let input = input.trim_end_matches('\n');
let mut iter = input.char_indices();
loop {
(i, ch) = iter.next()
.map(|(i, ch)| (i, Some(ch)))
.unwrap_or((input.len(), None))
;
match state {
State::Default(start) => {
match ch {
Some('+') | Some('-') => {
if start != i {
if let Some(_) = current_ind {return Err(
EdError::IndicesUnrelated{
prior_index: input[..start].to_owned(),
unrelated_index: input[start..i].to_owned(),
}
)}
let literal = input[start .. i].parse::<usize>()
.map_err(|_|EdError::IndexNotInt(input[start..i].to_owned()))?;
current_ind = Some(Ind::Literal(literal));
}
state = State::Offset(i + 1, ch == Some('-'));
},
Some('/') | Some('\'') | Some('?') | Some('.') | Some('$') => {
let c = ch.unwrap();
if start != i { return Err(EdError::IndexSpecialAfterStart{
prior_index: input[start..i].to_owned(),
special_index: c,
}); }
match c {
'\'' => {
state = State::Tag;
},
'/' => {
state = State::Pattern(i + 1); },
'?' => {
state = State::RevPattern(i + 1); },
'.' | '$' => {
if let Some(_) = current_ind { return Err(
EdError::IndicesUnrelated{
prior_index: input[..i].to_owned(),
unrelated_index: input[i..i+1].to_owned(),
}
)}
current_ind = Some(
if c == '.' { Ind::Selection } else { Ind::BufferLen }
);
state = State::Default(i + 1); },
_ => ed_unreachable!()?,
}
}
_ => {
if ch.is_some_and(|x| x.is_ascii_digit()) {
}
else {
return if start < i {
if let Some(_) = current_ind { Err(
EdError::IndicesUnrelated{
prior_index: input[..start].to_owned(),
unrelated_index: input[start..i].to_owned(),
}
)}
else {
let literal = input[start..i].parse::<usize>()
.map_err(|_|EdError::IndexNotInt(input[start..i].to_owned()))?;
Ok((i, Some(Ind::Literal(literal))))
}
}
else {
Ok((i, current_ind))
}
}
},
}
},
State::Tag => {
if let Some(_) = current_ind { return Err(
EdError::IndicesUnrelated{
prior_index: input[..i-1].to_owned(),
unrelated_index: {
let mut index = String::new();
for ch in input[i-1..].chars().take(2) {
index.push(ch);
}
index
},
}
)}
if let Some(c) = ch {
current_ind = Some(Ind::Tag(c));
state = State::Default( i + c.len_utf8() );
}
else {
return Err(EdError::IndexUnfinished("\'".to_string()));
}
},
State::Pattern(start) => {
if ch.is_none_or(|x| x == '/') {
if let Some(_) = current_ind { return Err(
EdError::IndicesUnrelated{
prior_index: input[..start-1].to_owned(),
unrelated_index: input[start-1..input.len().min(i+1)].to_owned(),
}
)}
current_ind = Some(Ind::Pattern(&input[start .. i]));
state = State::Default( i + 1 );
}
},
State::RevPattern(start) => {
if ch.is_none_or(|x| x == '?') {
if let Some(_) = current_ind { return Err(
EdError::IndicesUnrelated{
prior_index: input[..start-1].to_owned(),
unrelated_index: input[start-1..input.len().min(i+1)].to_owned(),
}
)}
current_ind = Some(Ind::RevPattern(&input[start .. i]));
state = State::Default( i + 1 );
}
},
State::Offset(start, negative) => {
match ch {
Some('+') | Some('-') => {
let offset = if start != i {
input[start .. i].parse::<usize>()
.map_err(|_|EdError::OffsetNotInt(input[start..i].to_owned()))?
} else { 1 };
current_ind = Some( if negative {
Ind::Sub(Box::new(current_ind.unwrap_or(Ind::Selection)), offset)
} else {
Ind::Add(Box::new(current_ind.unwrap_or(Ind::Selection)), offset)
});
state = State::Offset( i + ch.unwrap().len_utf8(), ch == Some('-') );
},
x if x.is_some_and(|x| x.is_ascii_digit()) => {}, _ => { let offset = if start < i {
input[start .. i].parse::<usize>()
.map_err(|_|EdError::OffsetNotInt(input[start .. i].to_owned()))?
} else {
1
};
return Ok((i, Some(
if negative {
Ind::Sub(Box::new(current_ind.unwrap_or(Ind::Selection)), offset)
} else {
Ind::Add(Box::new(current_ind.unwrap_or(Ind::Selection)), offset)
}
)))
},
}
},
} } }
pub fn parse_selection(
input: &str,
) -> Result<(usize, Option<Sel<'_>>)> {
let (offset, ind) = parse_index(input)?;
match input[offset .. ].chars().next() {
Some(';') => {
let (offset2, ind2) = parse_index(&input[offset + 1 ..])?;
let unwrapped1 = ind.unwrap_or(Ind::Selection);
let unwrapped2 = ind2.unwrap_or(Ind::BufferLen);
Ok((offset2 + 1 + offset, Some(Sel::Pair(unwrapped1, unwrapped2))))
},
Some(',') => {
let (offset2, ind2) = parse_index(&input[offset + 1 ..])?;
let unwrapped1 = ind.unwrap_or(Ind::Literal(1));
let unwrapped2 = ind2.unwrap_or(Ind::BufferLen);
Ok((offset2 + 1 + offset, Some(Sel::Pair(unwrapped1, unwrapped2))))
},
_ => { Ok(( offset, ind.map(Sel::Lone) ))
},
}
}
pub fn interpret_index(
state: &Ed<'_>,
index: Ind<'_>,
old_selection: usize,
) -> Result<usize> {
let ind = match index {
Ind::Selection => Ok(old_selection),
Ind::BufferLen => Ok(state.history.current().len()),
Ind::Literal(i) => Ok(i),
Ind::Tag(tag) => super::get_tag(state.history.current(), tag),
Ind::Pattern(pattern) =>
super::get_matching(
state.history.current(),
pattern,
old_selection,
super::Direction::Forwards,
),
Ind::RevPattern(pattern) =>
super::get_matching(
state.history.current(),
pattern,
old_selection,
super::Direction::Backwards
),
Ind::Add(inner, offset) => {
let inner = interpret_index(state, *inner, old_selection)?;
Ok(inner.saturating_add(offset))
},
Ind::Sub(inner, offset) => {
let inner = interpret_index(state, *inner, old_selection)?;
Ok(inner.saturating_sub(offset))
},
}?;
Ok(ind)
}
pub fn interpret_selection(
state: &Ed<'_>,
input: Option<Sel<'_>>,
old_selection: (usize, usize),
) -> Result<(usize, usize)> {
let selection = input.unwrap_or(Sel::Pair( Ind::Selection, Ind::Selection ));
let interpreted = match selection {
Sel::Lone(ind) => {
let i = interpret_index(state, ind, old_selection.0 )?;
(i, i)
},
Sel::Pair(ind1, ind2) => {
let i = interpret_index(state, ind1, old_selection.0 )?;
let i2 = interpret_index(state, ind2, old_selection.1 )?;
(i, i2)
},
};
Ok(interpreted)
}
pub fn interpret_index_from_selection(
state: &Ed<'_>,
selection: Option<Sel<'_>>,
old_selection: (usize, usize),
appends: bool,
) -> Result<usize> {
let selection = selection.unwrap_or(Sel::Lone(
Ind::Selection
));
let default = if appends { old_selection.1 } else { old_selection.0 };
Ok(match selection {
Sel::Lone(ind) => {
interpret_index(state, ind, default)?
},
Sel::Pair(ind1, ind2) => {
let ind = if appends { ind2 } else { ind1 };
interpret_index(state, ind, default)?
},
})
}