use crate::core::{EditorDocument, Range, Result};
#[cfg(feature = "rope")]
use crate::core::Position;
#[cfg(not(feature = "std"))]
use alloc::string::{String, ToString};
#[cfg(all(not(feature = "std"), feature = "rope"))]
use alloc::vec::Vec;
pub struct SelectRange<'a> {
document: &'a mut EditorDocument,
range: Range,
}
impl<'a> SelectRange<'a> {
pub(crate) fn new(document: &'a mut EditorDocument, range: Range) -> Self {
Self { document, range }
}
pub fn replace_with(self, text: &str) -> Result<&'a mut EditorDocument> {
self.document.replace(self.range, text)?;
Ok(self.document)
}
pub fn delete(self) -> Result<&'a mut EditorDocument> {
self.document.delete(self.range)?;
Ok(self.document)
}
pub fn wrap_with_tag(self, open_tag: &str, close_tag: &str) -> Result<&'a mut EditorDocument> {
let selected = self
.document
.rope()
.byte_slice(self.range.start.offset..self.range.end.offset);
let mut wrapped =
String::with_capacity(open_tag.len() + selected.len_bytes() + close_tag.len());
wrapped.push_str(open_tag);
wrapped.push_str(&selected.to_string());
wrapped.push_str(close_tag);
self.document.replace(self.range, &wrapped)?;
Ok(self.document)
}
#[cfg(feature = "rope")]
pub fn indent(self, spaces: usize) -> Result<&'a mut EditorDocument> {
let start_line = self.document.rope().byte_to_line(self.range.start.offset);
let end_line = self.document.rope().byte_to_line(self.range.end.offset);
let indent = " ".repeat(spaces);
let mut line_positions = Vec::new();
for line_idx in (start_line..=end_line).rev() {
let line_start = self.document.rope().line_to_byte(line_idx);
line_positions.push(line_start);
}
for line_start in line_positions {
let pos = Position::new(line_start);
let range = Range::empty(pos);
self.document.replace(range, &indent)?;
}
Ok(self.document)
}
#[cfg(feature = "rope")]
pub fn unindent(self, spaces: usize) -> Result<&'a mut EditorDocument> {
let start_line = self.document.rope().byte_to_line(self.range.start.offset);
let end_line = self.document.rope().byte_to_line(self.range.end.offset);
let mut unindent_ops = Vec::new();
for line_idx in (start_line..=end_line).rev() {
let line_start = self.document.rope().line_to_byte(line_idx);
let line = self.document.rope().line(line_idx);
let mut space_count = 0;
for ch in line.chars().take(spaces) {
if ch == ' ' {
space_count += 1;
} else {
break;
}
}
if space_count > 0 {
unindent_ops.push((line_start, space_count));
}
}
for (line_start, space_count) in unindent_ops {
let range = Range::new(
Position::new(line_start),
Position::new(line_start + space_count),
);
self.document.delete(range)?;
}
Ok(self.document)
}
pub fn text(&self) -> String {
self.document
.rope()
.byte_slice(self.range.start.offset..self.range.end.offset)
.to_string()
}
pub const fn range(&self) -> Range {
self.range
}
}