use super::{BufferId, Cursor, Position};
#[derive(Debug, Clone)]
pub struct BufferSnapshot {
pub id: BufferId,
pub lines: Vec<String>,
pub cursor: Cursor,
pub file_path: Option<String>,
pub modified: bool,
}
impl BufferSnapshot {
#[must_use]
pub fn from_buffer(buffer: &super::Buffer, cursor: Cursor) -> Self {
Self {
id: buffer.id(),
lines: buffer.lines().to_vec(),
cursor,
file_path: buffer.file_path().map(String::from),
modified: buffer.is_modified(),
}
}
#[must_use]
pub const fn new(
id: BufferId,
lines: Vec<String>,
cursor: Cursor,
file_path: Option<String>,
modified: bool,
) -> Self {
Self {
id,
lines,
cursor,
file_path,
modified,
}
}
#[must_use]
pub const fn line_count(&self) -> usize {
self.lines.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.lines.is_empty()
}
#[must_use]
pub fn line(&self, idx: usize) -> Option<&str> {
self.lines.get(idx).map(String::as_str)
}
#[must_use]
pub fn line_len(&self, idx: usize) -> Option<usize> {
self.lines.get(idx).map(|l| l.chars().count())
}
#[must_use]
pub fn lines(&self) -> &[String] {
&self.lines
}
#[must_use]
pub fn content(&self) -> String {
self.lines.join("\n")
}
#[must_use]
pub fn text_in_range(&self, start: Position, end: Position) -> String {
if self.lines.is_empty() {
return String::new();
}
let (start, end) = if start <= end {
(start, end)
} else {
(end, start)
};
let start_line = start.line.min(self.lines.len() - 1);
let end_line = end.line.min(self.lines.len() - 1);
if start_line == end_line {
let line = &self.lines[start_line];
let chars: Vec<char> = line.chars().collect();
let start_col = start.column.min(chars.len());
let end_col = end.column.min(chars.len());
return chars[start_col..end_col].iter().collect();
}
let mut result = String::new();
for line_idx in start_line..=end_line {
let line = &self.lines[line_idx];
let chars: Vec<char> = line.chars().collect();
if line_idx == start_line {
let start_col = start.column.min(chars.len());
result.extend(&chars[start_col..]);
result.push('\n');
} else if line_idx == end_line {
let end_col = end.column.min(chars.len());
result.extend(&chars[..end_col]);
} else {
result.push_str(line);
result.push('\n');
}
}
result
}
#[must_use]
pub const fn position(&self) -> Position {
self.cursor.position
}
#[must_use]
pub fn is_valid_position(&self, pos: Position) -> bool {
if pos.line >= self.lines.len() {
return false;
}
self.line(pos.line)
.is_some_and(|line| pos.column <= line.chars().count())
}
}