1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
//! Contains the Buffer trait and any build in implementations.
// General implementations for file interaction and substitution of e.g. '\n'
pub mod file;
pub mod substitute;
// Include a general test
// Doesn't test file handling to allow buffers to act on non-file paths
#[cfg(test)]
mod test;
// Include the buffer implementations based on features
#[cfg(feature = "vecbuffer")]
mod vecbuffer;
#[cfg(feature = "vecbuffer")]
pub use vecbuffer::*;
/// Trait that defines a buffer supporting 'ed's base commands
///
/// BEWARE!!! 1-indexed!
/// This means _line_ 0 doesn't exist, error if given (use verify_selection/verify_line below)
/// BUT, _index_ 0 is valid (therefore use verify_index instead)
/// Subtract 1 to get 0 indexed. It is recommended to use .saturating_sub(1)
pub trait Buffer {
// Functions for resolving and verifying indices in the parser
/// Return the number of lines stored in the buffer
fn len(&self)
-> usize ;
/// Get line tagged with given letter. Not found is error
fn get_tag(&self, tag: char)
-> Result<usize, &'static str> ;
/// Return the nearest previous/following index in the selection that contains the regex pattern
fn get_matching(&self, pattern: &str, curr_line: usize, backwards: bool)
-> Result<usize, &'static str> ;
// Regex matching for the macro commands ('g', 'v', 'G', 'V')
/// Set the matched flag on all lines matching given pattern
fn mark_matching(&mut self, pattern: &str, selection: (usize, usize), inverse: bool)
-> Result<(), &'static str> ;
/// Get a line with the matched flag set, clearing that line's flag
fn get_marked(&mut self)
-> Result<Option<usize>, &'static str> ;
// Simple buffer modifications, but with possibly complex storage
/// Mark a line with a letter, mark with '\0' to clear
fn tag_line(&mut self, index: usize, tag: char)
-> Result<(), &'static str> ;
/// Takes a iterator over lines in strings and inserts after given index
fn insert<'a>(&mut self, data: &mut dyn Iterator<Item = &'a str>, index: usize)
-> Result<(), &'static str> ;
/// Cut the selection from the buffer, into the clipboard
fn cut(&mut self, selection: (usize, usize))
-> Result<(), &'static str> ;
/// Replace selection with input
fn change<'a>(&mut self, data: &mut dyn Iterator<Item = &'a str>, selection: (usize, usize))
-> Result<(), &'static str> ;
/// Move selection to immediately after index
fn mov(&mut self, selection: (usize, usize), index: usize)
-> Result<(), &'static str> ;
/// Insert a copy of the selection immediately after index
fn mov_copy(&mut self, selection: (usize, usize), index: usize)
-> Result<(), &'static str> ;
/// Join all lines in selection into one line
fn join(&mut self, selection: (usize, usize))
-> Result<(), &'static str> ;
/// Copy selected lines into clipboard
fn copy(&mut self, selection: (usize, usize))
-> Result<(), &'static str> ;
/// Paste the clipboard contents after given index
/// Leave clipboard unchanged
fn paste(&mut self, index: usize)
-> Result<usize, &'static str> ;
/// Perform regex search and replace on the selection changing pattern.0 to pattern.1
/// Should interpret escape sequences in both patterns. At minimum \n and \\.
/// If pattern is empty, should re-use stored pattern from previous s command
/// Returns new end of selection, since it may delete or add lines
/// Beware that it may delete the whole selection just as 'c'
fn search_replace(&mut self, pattern: (&str, &str), selection: (usize, usize), global: bool)
-> Result<usize, &'static str> ;
// Save/load commands. Here to enable creative Buffers, such as ssh+sed for remote editing
/// Read to the buffer from given path
/// If index is None replaces current buffer with read lines
/// Else inserts read lines after given index
/// Return number of lines read
fn read_from(&mut self, path: &str, index: Option<usize>, must_exist: bool)
-> Result<usize, &'static str> ;
/// Write the buffer to given path
fn write_to(&mut self, selection: Option<(usize, usize)>, path: &str, append: bool)
-> Result<(), &'static str> ;
/// Returns true if no changes have been made since last saving
fn saved(&self)
-> bool ;
// Finally, the basic output command.
/// Return the given selection without any formatting
fn get_selection<'a>(&'a self, selection: (usize, usize))
-> Result<Box<dyn Iterator<Item = &'a str> + 'a>, &'static str> ;
}
// General index, line and selection validation functions
// These are good to run before using arguments to your buffer
/// Verify that the index is between 0 and buffer.len() inclusive.
///
/// That means it is valid to _append_ to the index in question,
/// but it may not be valid to read from.
pub fn verify_index(
buffer: &impl Buffer,
index: usize,
) -> Result<(), &'static str> {
// Indices are valid at len.
// Needed to be able to append to the buffer via insert operations.
if index > buffer.len() { return Err(crate::error_consts::INDEX_TOO_BIG); }
Ok(())
}
/// Verify that index is between 1 and buffer.len() inclusive
///
/// This guarantees that the line exists, to both write to and read from.
/// Will always error if buffer.len() == 0, since no lines exist.
pub fn verify_line(
buffer: &impl Buffer,
index: usize,
) -> Result<(), &'static str> {
if index < 1 { Err(crate::error_consts::INVALID_LINENR0) }
else if index > buffer.len() { Err(crate::error_consts::INDEX_TOO_BIG) }
else { Ok(()) }
}
/// Verify that all lines in the selection exist and that it isn't empty.
///
/// Will always error if buffer.len() == 0, since no lines exist.
pub fn verify_selection(
buffer: &impl Buffer,
selection: (usize, usize),
) -> Result<(), &'static str> {
// Line 0 doesn't exist, even though index 0 is valid
if selection.0 == 0 { return Err(crate::error_consts::INVALID_LINENR0); }
// A selection must contain something to be valid
if selection.0 > selection.1 { return Err(crate::error_consts::SELECTION_EMPTY); }
// It cannot contain non-existent lines, such as index buffer.len() and beyond
if selection.1 > buffer.len() { return Err(crate::error_consts::INDEX_TOO_BIG); }
Ok(())
}