use crate::core::{EditorDocument, Position, Range, Result};
#[cfg(not(feature = "rope"))]
use crate::core::errors::EditorError;
#[cfg(all(not(feature = "std"), not(feature = "rope")))]
use alloc::string::ToString;
pub struct AtPosition<'a> {
document: &'a mut EditorDocument,
position: Position,
}
impl<'a> AtPosition<'a> {
pub(crate) fn new(document: &'a mut EditorDocument, position: Position) -> Self {
Self { document, position }
}
pub fn insert_text(self, text: &str) -> Result<&'a mut EditorDocument> {
let range = Range::empty(self.position);
self.document.replace(range, text)?;
Ok(self.document)
}
pub fn insert_line(self) -> Result<&'a mut EditorDocument> {
self.insert_text("\n")
}
pub fn delete(self, count: usize) -> Result<&'a mut EditorDocument> {
let end = self.position.advance(count);
let range = Range::new(self.position, end);
self.document.delete(range)?;
Ok(self.document)
}
pub fn backspace(self, count: usize) -> Result<&'a mut EditorDocument> {
let start = self.position.retreat(count);
let range = Range::new(start, self.position);
self.document.delete(range)?;
Ok(self.document)
}
pub fn replace_to_line_end(self, text: &str) -> Result<&'a mut EditorDocument> {
#[cfg(feature = "rope")]
{
let rope = self.document.rope();
let line_idx = rope.byte_to_line(self.position.offset);
let line_end_byte = if line_idx + 1 < rope.len_lines() {
rope.line_to_byte(line_idx + 1).saturating_sub(1)
} else {
rope.len_bytes()
};
let range = Range::new(self.position, Position::new(line_end_byte));
self.document.replace(range, text)?;
Ok(self.document)
}
#[cfg(not(feature = "rope"))]
{
Err(EditorError::FeatureNotEnabled {
feature: "line-based operations".to_string(),
required_feature: "rope".to_string(),
})
}
}
pub const fn position(&self) -> Position {
self.position
}
#[cfg(feature = "rope")]
pub fn to_line_column(&self) -> Result<(usize, usize)> {
let rope = self.document.rope();
let line_idx = rope.byte_to_line(self.position.offset);
let line_start = rope.line_to_byte(line_idx);
let col_offset = self.position.offset - line_start;
let line = rope.line(line_idx);
let mut char_col = 0;
let mut byte_count = 0;
for ch in line.chars() {
if byte_count >= col_offset {
break;
}
byte_count += ch.len_utf8();
char_col += 1;
}
Ok((line_idx + 1, char_col + 1)) }
}