ad_editor/exec/
cached_stdin.rs

1use crate::{
2    buffer::GapBuffer,
3    dot::Dot,
4    exec::{addr::Address, Edit},
5};
6use std::{
7    cell::RefCell,
8    io::{stdin, Stdin},
9};
10
11/// Initial length of the line buffer for when we read from stdin
12const LINE_BUF_LEN: usize = 100;
13
14/// A wrapper around stdin that buffers and caches input so that it can be manipulated
15/// like a Buffer
16#[derive(Debug)]
17pub struct CachedStdin {
18    inner: RefCell<CachedStdinInner>,
19    buf: RefCell<String>,
20    gb: RefCell<GapBuffer>,
21}
22
23#[derive(Debug)]
24struct CachedStdinInner {
25    stdin: Stdin,
26    closed: bool,
27}
28
29impl Default for CachedStdin {
30    fn default() -> Self {
31        Self::new()
32    }
33}
34
35impl CachedStdin {
36    /// Construct a new, empty [CachedStdin] ready to process input.
37    pub fn new() -> Self {
38        Self {
39            inner: RefCell::new(CachedStdinInner {
40                stdin: stdin(),
41                closed: false,
42            }),
43            buf: RefCell::new(String::with_capacity(LINE_BUF_LEN)),
44            gb: RefCell::new(GapBuffer::from("")),
45        }
46    }
47
48    fn is_closed(&self) -> bool {
49        self.inner.borrow().closed
50    }
51
52    fn get_char(&self, ix: usize) -> Option<char> {
53        self.gb.borrow().get_char(ix)
54    }
55
56    fn try_read_next_line(&self) {
57        let mut inner = self.inner.borrow_mut();
58        let mut buf = self.buf.borrow_mut();
59        buf.clear();
60
61        match inner.stdin.read_line(&mut buf) {
62            Ok(n) => {
63                let len = self.gb.borrow().len_chars();
64                self.gb.borrow_mut().insert_str(len, &buf);
65                inner.closed = n == 0;
66            }
67            Err(_) => inner.closed = true,
68        };
69    }
70}
71
72impl Address for CachedStdin {
73    fn current_dot(&self) -> Dot {
74        Dot::from_char_indices(0, usize::MAX)
75    }
76
77    fn len_chars(&self) -> usize {
78        self.gb.borrow().len_chars()
79    }
80
81    fn max_iter(&self) -> usize {
82        if self.is_closed() {
83            self.gb.borrow().len_chars()
84        } else {
85            usize::MAX
86        }
87    }
88
89    fn line_to_char(&self, line_idx: usize) -> Option<usize> {
90        let cur_len = self.gb.borrow().len_lines();
91
92        if line_idx > cur_len {
93            for _ in cur_len..=line_idx {
94                self.try_read_next_line();
95                if self.is_closed() {
96                    break;
97                }
98            }
99        }
100
101        self.gb.borrow().try_line_to_char(line_idx)
102    }
103
104    fn char_to_line(&self, char_idx: usize) -> Option<usize> {
105        self.gb.borrow().try_char_to_line(char_idx)
106    }
107
108    fn char_to_line_end(&self, char_idx: usize) -> Option<usize> {
109        let gb = self.gb.borrow();
110        let line_idx = gb.try_char_to_line(char_idx)?;
111        match gb.try_line_to_char(line_idx + 1) {
112            None => Some(gb.len_chars() - 1),
113            Some(idx) => Some(idx),
114        }
115    }
116
117    fn char_to_line_start(&self, char_idx: usize) -> Option<usize> {
118        let gb = self.gb.borrow();
119        let line_idx = gb.try_char_to_line(char_idx)?;
120        Some(gb.line_to_char(line_idx))
121    }
122}
123
124impl Edit for CachedStdin {
125    fn insert(&mut self, ix: usize, s: &str) {
126        self.gb.borrow_mut().insert_str(ix, s)
127    }
128
129    fn remove(&mut self, from: usize, to: usize) {
130        self.gb.borrow_mut().remove_range(from, to)
131    }
132}
133
134#[derive(Debug)]
135pub struct CachedStdinIter<'a> {
136    pub(super) inner: &'a CachedStdin,
137    pub(super) from: usize,
138    pub(super) to: usize,
139}
140
141impl Iterator for CachedStdinIter<'_> {
142    type Item = (usize, char);
143
144    fn next(&mut self) -> Option<Self::Item> {
145        // self.from == self.to - 1 is the last character so
146        // we catch end of iteration on the subsequent call
147        if self.from >= self.to {
148            return None;
149        }
150
151        loop {
152            match self.inner.get_char(self.from) {
153                Some(ch) => {
154                    let res = (self.from, ch);
155                    self.from += 1;
156                    return Some(res);
157                }
158                None if self.inner.is_closed() => return None,
159                None => self.inner.try_read_next_line(),
160            }
161        }
162    }
163}