pub mod edit;
pub mod view;
use crate::editor::cursor::{Cursor, Motion, Position};
use crate::editor::document::Document;
use crate::syntax::{self, HighlightCache};
use crate::undo::History;
pub use view::View;
#[derive(Debug)]
pub struct Buffer {
pub document: Document,
pub view: View,
cursors: Vec<Cursor>,
primary: usize,
pub history: History,
pub syntax: HighlightCache,
}
impl Buffer {
#[must_use]
pub fn new(document: Document) -> Self {
let syntax = HighlightCache::new(document.path().and_then(syntax::detect));
Self {
document,
view: View::default(),
cursors: vec![Cursor::at(Position::ZERO)],
primary: 0,
history: History::default(),
syntax,
}
}
pub fn detect_language(&mut self) {
self.syntax
.set_language(self.document.path().and_then(syntax::detect));
}
pub fn invalidate_syntax_from(&mut self, line: usize) {
self.syntax.invalidate_from(line);
}
#[must_use]
pub fn empty() -> Self {
Self::new(Document::new())
}
#[must_use]
pub fn cursor(&self) -> Cursor {
self.cursors[self.primary]
}
pub fn cursor_mut(&mut self) -> &mut Cursor {
&mut self.cursors[self.primary]
}
#[must_use]
pub fn cursors(&self) -> &[Cursor] {
&self.cursors
}
pub fn move_cursors(&mut self, motion: Motion, extend: bool, allow_eol: bool) {
for cursor in &mut self.cursors {
cursor.apply(motion, &self.document, extend, allow_eol);
}
self.resort();
}
pub fn collapse_selections(&mut self) {
for cursor in &mut self.cursors {
cursor.collapse();
}
}
pub fn anchor_selections(&mut self) {
for cursor in &mut self.cursors {
cursor.anchor_here();
}
}
pub fn add_cursor(&mut self, cursor: Cursor) {
if self.cursors.iter().any(|c| c.head == cursor.head) {
return;
}
self.cursors.push(cursor);
self.resort();
}
pub fn clear_secondary_cursors(&mut self) {
let primary = self.cursors[self.primary];
self.cursors.clear();
self.cursors.push(primary);
self.primary = 0;
}
#[must_use]
pub fn edit_order(&self) -> Vec<usize> {
(0..self.cursors.len()).rev().collect()
}
pub fn clamp_cursors(&mut self, allow_eol: bool) {
for cursor in &mut self.cursors {
cursor.head = self.document.clamp(cursor.head, allow_eol);
cursor.anchor = self.document.clamp(cursor.anchor, allow_eol);
}
self.resort();
}
fn resort(&mut self) {
if self.cursors.len() == 1 {
self.primary = 0;
return;
}
let primary_head = self.cursors[self.primary].head;
self.cursors.sort_by_key(|c| c.head);
self.cursors.dedup_by_key(|c| c.head);
self.primary = self
.cursors
.iter()
.position(|c| c.head == primary_head)
.unwrap_or(0);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn buffer(text: &str) -> Buffer {
Buffer::new(Document::from_text(text, None))
}
#[test]
fn a_new_buffer_has_exactly_one_cursor() {
let buf = buffer("abc");
assert_eq!(buf.cursors().len(), 1);
assert_eq!(buf.cursor().head, Position::ZERO);
}
#[test]
fn cursors_stay_in_document_order() {
let mut buf = buffer("aaa\nbbb\nccc");
buf.add_cursor(Cursor::at(Position::new(2, 1)));
buf.add_cursor(Cursor::at(Position::new(1, 1)));
let heads: Vec<_> = buf.cursors().iter().map(|c| c.head).collect();
assert_eq!(
heads,
vec![Position::ZERO, Position::new(1, 1), Position::new(2, 1)]
);
assert_eq!(buf.cursor().head, Position::ZERO);
}
#[test]
fn duplicate_cursors_are_rejected() {
let mut buf = buffer("abc");
buf.add_cursor(Cursor::at(Position::ZERO));
assert_eq!(buf.cursors().len(), 1);
}
#[test]
fn collapsing_cursors_keeps_the_primary_one() {
let mut buf = buffer("aaa\nbbb");
buf.add_cursor(Cursor::at(Position::new(1, 2)));
buf.clear_secondary_cursors();
assert_eq!(buf.cursors().len(), 1);
assert_eq!(buf.cursor().head, Position::ZERO);
}
#[test]
fn merged_cursors_do_not_leave_a_dangling_primary() {
let mut buf = buffer("abc");
buf.add_cursor(Cursor::at(Position::new(0, 1)));
buf.move_cursors(Motion::LineEnd, false, false);
assert_eq!(buf.cursors().len(), 1);
assert_eq!(buf.cursor().head, Position::new(0, 2));
}
#[test]
fn edit_order_runs_backwards_through_the_document() {
let mut buf = buffer("aaa\nbbb\nccc");
buf.add_cursor(Cursor::at(Position::new(1, 0)));
buf.add_cursor(Cursor::at(Position::new(2, 0)));
assert_eq!(buf.edit_order(), vec![2, 1, 0]);
}
}