aboutsummaryrefslogtreecommitdiffstats
path: root/src/line
diff options
context:
space:
mode:
Diffstat (limited to 'src/line')
-rw-r--r--src/line/buf.rs150
-rw-r--r--src/line/mod.rs219
2 files changed, 369 insertions, 0 deletions
diff --git a/src/line/buf.rs b/src/line/buf.rs
new file mode 100644
index 0000000..420546a
--- /dev/null
+++ b/src/line/buf.rs
@@ -0,0 +1,150 @@
+use crate::cursor::*;
+use std::io::Write;
+
+pub struct LineBuf {
+ pre: Vec<u8>,
+ post: Vec<u8>,
+ dirty: bool,
+}
+
+impl Default for LineBuf {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+#[allow(unused)]
+impl LineBuf {
+ pub fn new() -> Self {
+ Self {
+ pre: Vec::new(),
+ post: Vec::new(),
+ dirty: false,
+ }
+ }
+
+ pub fn del_left(&mut self) -> Option<u8> {
+ self.dirty = true;
+ self.pre.pop()
+ }
+
+ pub fn del_right(&mut self) -> Option<u8> {
+ self.dirty = true;
+ self.post.pop()
+ }
+
+ pub fn len(&self) -> usize {
+ self.pre.len() + self.post.len()
+ }
+
+ pub fn left(&mut self) -> bool {
+ if let Some(byte) = self.del_left() {
+ self.post.push(byte);
+ true
+ } else {
+ false
+ }
+ }
+
+ pub fn right(&mut self) -> bool {
+ if let Some(byte) = self.del_right() {
+ self.pre.push(byte);
+ true
+ } else {
+ false
+ }
+ }
+
+ pub fn all_left(&mut self) -> usize {
+ let n = self.pre.len();
+ while self.left() {}
+ n
+ }
+
+ pub fn all_right(&mut self) -> usize {
+ let n = self.post.len();
+ while self.right() {}
+ n
+ }
+
+ pub fn get_left(&self) -> Option<u8> {
+ self.pre.last().cloned()
+ }
+
+ pub fn get_right(&self) -> Option<u8> {
+ self.post.last().cloned()
+ }
+
+ pub fn add(&mut self, chr: u8) {
+ self.dirty = true;
+ self.pre.push(chr);
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.pre.is_empty() && self.post.is_empty()
+ }
+
+ pub fn is_dirty(&self) -> bool {
+ self.dirty
+ }
+
+ /// sets content all to the left
+ pub fn set_content(&mut self, buf: Vec<u8>) {
+ self.pre = buf;
+ self.post = Vec::new();
+ }
+
+ pub fn pre(&self) -> &[u8] {
+ &self.pre
+ }
+
+ pub fn into_bytes(&self) -> Vec<u8> {
+ let mut buf = Vec::with_capacity(self.pre.len() + self.post.len());
+ buf.extend_from_slice(&self.pre);
+ for b in self.post.iter().rev() {
+ buf.push(*b);
+ }
+ buf
+ }
+
+ pub fn distance_from_right_end(&self) -> usize {
+ self.post.len()
+ }
+
+ /// returns the whole contents of the buffer, and empties it in the process
+ pub fn dump(&mut self) -> Vec<u8> {
+ while self.right() {}
+ let mut buf = Vec::new();
+ core::mem::swap(&mut self.pre, &mut buf);
+ self.dirty = false;
+ buf
+ }
+
+ pub fn clear(&mut self) {
+ self.pre.clear();
+ self.post.clear();
+ self.dirty = false;
+ }
+
+ pub fn display_pre(&self) {
+ std::io::stdout().write_all(&self.pre).unwrap();
+ }
+
+ /// TODO: kinda ugly that this is here
+ pub fn display_post(&self, post: &[u8]) {
+ for &x in self.post.iter().rev() {
+ std::io::stdout().write_all(&[x]).unwrap();
+ }
+ std::io::stdout().write_all(post).unwrap();
+ move_cursor(Direction::Left, self.post.len() + post.len());
+ std::io::stdout().flush().unwrap();
+ }
+
+ pub fn left_len(&self) -> usize {
+ self.pre.len()
+ }
+
+ pub fn right_len(&self) -> usize {
+ self.post.len()
+ }
+}
diff --git a/src/line/mod.rs b/src/line/mod.rs
new file mode 100644
index 0000000..72f2fe1
--- /dev/null
+++ b/src/line/mod.rs
@@ -0,0 +1,219 @@
+mod buf;
+
+use std::io::{self, Write};
+
+use crate::cursor::{self, Direction};
+pub use buf::LineBuf;
+
+pub struct Line {
+ buf: LineBuf,
+ dirty: bool,
+}
+
+impl std::ops::Deref for Line {
+ type Target = LineBuf;
+
+ fn deref(&self) -> &Self::Target {
+ &self.buf
+ }
+}
+
+impl Line {
+ pub fn new() -> Self {
+ Self {
+ buf: LineBuf::new(),
+ dirty: false,
+ }
+ }
+
+ pub fn mark_clean(&mut self) {
+ self.dirty = false;
+ }
+
+ pub fn is_dirty(&self) -> bool {
+ self.dirty
+ }
+
+ pub fn had_contents(&self) -> bool {
+ self.buf.is_dirty()
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.buf.is_empty()
+ }
+
+ pub fn cursor_left(&mut self) -> io::Result<()> {
+ if self.buf.left() {
+ let mut stdout = io::stdout().lock();
+ cursor::fmove_cursor(Direction::Left, 1, &mut stdout)?;
+ stdout.flush()?;
+ }
+ Ok(())
+ }
+
+ pub fn cursor_right(&mut self) -> io::Result<()> {
+ if self.buf.right() {
+ let mut stdout = io::stdout().lock();
+ cursor::fmove_cursor(Direction::Right, 1, &mut stdout)?;
+ stdout.flush()?;
+ }
+ Ok(())
+ }
+
+ pub fn move_to_begin(&mut self) -> io::Result<()> {
+ let mut stdout = io::stdout().lock();
+ cursor::fmove_cursor(Direction::Left, self.buf.all_left(), &mut stdout)?;
+ stdout.flush()
+ }
+
+ pub fn move_to_end(&mut self) -> io::Result<()> {
+ let mut stdout = io::stdout().lock();
+ cursor::fmove_cursor(Direction::Right, self.buf.all_right(), &mut stdout)?;
+ stdout.flush()
+ }
+
+ // move to next
+ pub fn cursor_left_word(&mut self) -> io::Result<()> {
+ let mut i = 0;
+
+ // find word
+ while let Some(b' ') = self.buf.get_left() {
+ self.buf.left();
+ i += 1;
+ }
+
+ // skip it
+ while let Some(x) = self.buf.get_left()
+ && !x.is_ascii_whitespace()
+ {
+ self.buf.left();
+ i += 1;
+ }
+
+ let mut stdout = io::stdout().lock();
+ cursor::fmove_cursor(Direction::Left, i, &mut stdout)?;
+ stdout.flush()
+ }
+
+ pub fn cursor_right_word(&mut self) -> io::Result<()> {
+ let mut i = 0;
+
+ // find word
+ while let Some(b' ') = self.buf.get_right() {
+ self.buf.right();
+ i += 1;
+ }
+
+ // skip it
+ while let Some(x) = self.buf.get_right()
+ && !x.is_ascii_whitespace()
+ {
+ self.buf.right();
+ i += 1;
+ }
+
+ let mut stdout = io::stdout().lock();
+ cursor::fmove_cursor(Direction::Right, i, &mut stdout)?;
+ stdout.flush()
+ }
+}
+
+macro_rules! mutating_impls {
+ ($(pub fn $fun:ident(&mut $self:ident $($arg:tt)*) -> $ty:ty $body:block )*) => {
+ impl Line {$(
+ pub fn $fun(&mut $self $($arg)*) -> $ty {
+ $self.dirty = true;
+ $body
+ }
+ )*}
+ };
+}
+
+mutating_impls! {
+ pub fn clear_prompt(&mut self) -> io::Result<()> {
+ let mut stdout = io::stdout().lock();
+ cursor::fmove_cursor(
+ Direction::Right,
+ self.buf.distance_from_right_end(),
+ &mut stdout,
+ )?;
+ for _ in 0..self.buf.len() {
+ stdout.write_all(b"\x08 \x08")?;
+ }
+ self.buf.clear();
+ stdout.flush()
+ }
+
+ pub fn type_bytes(&mut self, bs: &[u8]) -> io::Result<()> {
+ for b in bs.iter() {
+ self.buf.add(*b);
+ }
+ io::stdout().lock().write_all(&bs)?;
+ self.buf.display_post(b"");
+ Ok(())
+ }
+
+ pub fn type_byte(&mut self, b: u8) -> io::Result<()> {
+ self.type_bytes(&[b])
+ }
+
+ pub fn del_left(&mut self) -> io::Result<()> {
+ if self.buf.del_left().is_some() {
+ cursor::fmove_cursor(Direction::Left, 1, &mut io::stdout())?;
+ self.buf.display_post(b" ");
+ }
+ Ok(())
+ }
+
+ pub fn del_right(&mut self) -> io::Result<()> {
+ self.buf.del_right();
+ self.buf.display_post(b" ");
+ Ok(())
+ }
+
+ pub fn del_left_word(&mut self) -> io::Result<()> {
+ let mut del = 0;
+ while let Some(x) = self.buf.get_left()
+ && x == b' '
+ {
+ self.buf.del_left();
+ del += 1;
+ }
+ while let Some(x) = self.buf.get_left()
+ && x != b' '
+ {
+ self.buf.del_left();
+ del += 1;
+ }
+ cursor::fmove_cursor(Direction::Left, del, &mut io::stdout())?;
+ self.buf.display_post(&vec![b' '; del]);
+ Ok(())
+ }
+
+ pub fn del_right_word(&mut self) -> io::Result<()> {
+ let mut del = 0;
+ while let Some(x) = self.buf.get_right()
+ && x == b' '
+ {
+ self.buf.del_right();
+ del += 1;
+ }
+ while let Some(x) = self.buf.get_right()
+ && x != b' '
+ {
+ self.buf.del_right();
+ del += 1;
+ }
+ self.buf.display_post(&vec![b' '; del]);
+ Ok(())
+ }
+
+ pub fn set_content(&mut self, content: Vec<u8>) -> io::Result<()> {
+ self.buf.set_content(content);
+ Ok(())
+ }
+
+ pub fn dump(&mut self) -> Vec<u8> {
+ self.buf.dump()
+ }
+}