#[derive(Debug, Clone, Default)]
#[allow(dead_code)]
pub(crate) struct InputBuffer {
text: String, cursor: usize, }
#[allow(dead_code)]
fn char_to_byte_idx(text: &str, char_idx: usize) -> usize {
match text.char_indices().nth(char_idx) {
Some((idx, _)) => idx,
None => text.len(),
}
}
#[allow(dead_code)]
impl InputBuffer {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn text(&self) -> &str {
&self.text
}
pub(crate) fn is_empty(&self) -> bool {
self.text.is_empty()
}
pub(crate) fn cursor(&self) -> usize {
self.cursor
}
pub(crate) fn clear(&mut self) {
self.text.clear();
self.cursor = 0;
}
pub(crate) fn set_text(&mut self, text: impl Into<String>) {
self.text = text.into();
self.cursor = self.text.chars().count();
}
pub(crate) fn insert_char(&mut self, c: char) {
let byte_idx = char_to_byte_idx(&self.text, self.cursor);
self.text.insert(byte_idx, c);
self.cursor += 1;
}
pub(crate) fn insert_str(&mut self, s: &str) {
let byte_idx = char_to_byte_idx(&self.text, self.cursor);
self.text.insert_str(byte_idx, s);
self.cursor += s.chars().count();
}
pub(crate) fn insert_newline(&mut self) {
self.insert_char('\n');
}
pub(crate) fn backspace(&mut self) {
if self.cursor == 0 {
return;
}
let start_byte = char_to_byte_idx(&self.text, self.cursor - 1);
let end_byte = char_to_byte_idx(&self.text, self.cursor);
self.text.replace_range(start_byte..end_byte, "");
self.cursor -= 1;
}
pub(crate) fn delete(&mut self) {
let char_count = self.text.chars().count();
if self.cursor >= char_count {
return;
}
let start_byte = char_to_byte_idx(&self.text, self.cursor);
let end_byte = char_to_byte_idx(&self.text, self.cursor + 1);
self.text.replace_range(start_byte..end_byte, "");
}
pub(crate) fn move_left(&mut self) {
self.cursor = self.cursor.saturating_sub(1);
}
pub(crate) fn move_right(&mut self) {
let char_count = self.text.chars().count();
if self.cursor < char_count {
self.cursor += 1;
}
}
pub(crate) fn move_word_left(&mut self) {
let chars: Vec<char> = self.text.chars().collect();
let mut i = self.cursor;
while i > 0 && chars[i - 1].is_whitespace() {
i -= 1;
}
while i > 0 && !chars[i - 1].is_whitespace() {
i -= 1;
}
self.cursor = i;
}
pub(crate) fn move_word_right(&mut self) {
let chars: Vec<char> = self.text.chars().collect();
let len = chars.len();
let mut i = self.cursor;
while i < len && chars[i].is_whitespace() {
i += 1;
}
while i < len && !chars[i].is_whitespace() {
i += 1;
}
self.cursor = i;
}
pub(crate) fn move_home(&mut self) {
let chars: Vec<char> = self.text.chars().collect();
let mut i = self.cursor;
while i > 0 {
if chars[i - 1] == '\n' {
break;
}
i -= 1;
}
self.cursor = i;
}
pub(crate) fn move_end(&mut self) {
let chars: Vec<char> = self.text.chars().collect();
let len = chars.len();
let mut i = self.cursor;
while i < len {
if chars[i] == '\n' {
break;
}
i += 1;
}
self.cursor = i;
}
pub(crate) fn kill_to_line_end(&mut self) {
let char_count = self.text.chars().count();
if self.cursor >= char_count {
return;
}
let chars: Vec<char> = self.text.chars().collect();
let mut end = self.cursor;
while end < char_count && chars[end] != '\n' {
end += 1;
}
if end == self.cursor && end < char_count {
end += 1;
}
if end > self.cursor {
let start_byte = char_to_byte_idx(&self.text, self.cursor);
let end_byte = char_to_byte_idx(&self.text, end);
self.text.replace_range(start_byte..end_byte, "");
}
}
pub(crate) fn delete_word_left(&mut self) {
let end = self.cursor;
self.move_word_left();
let start = self.cursor;
if start < end {
let start_byte = char_to_byte_idx(&self.text, start);
let end_byte = char_to_byte_idx(&self.text, end);
self.text.replace_range(start_byte..end_byte, "");
}
}
pub(crate) fn kill_to_line_start(&mut self) {
let end = self.cursor;
self.move_home();
let start = self.cursor;
if start < end {
let start_byte = char_to_byte_idx(&self.text, start);
let end_byte = char_to_byte_idx(&self.text, end);
self.text.replace_range(start_byte..end_byte, "");
}
}
pub(crate) fn lines(&self) -> Vec<&str> {
self.text.split('\n').collect()
}
pub(crate) fn cursor_line_col(&self) -> (usize, usize) {
let mut line = 0;
let mut col = 0;
for c in self.text.chars().take(self.cursor) {
if c == '\n' {
line += 1;
col = 0;
} else {
col += 1;
}
}
(line, col)
}
}
#[cfg(test)]
mod input_tests;