use crate::completion::char_to_byte;
const INDENT: &str = " ";
#[must_use]
pub fn insert(text: &str, cursor: usize, insertion: &str) -> (String, usize) {
let byte = char_to_byte(text, cursor);
let mut result = text.to_owned();
result.insert_str(byte, insertion);
(result, cursor + insertion.chars().count())
}
#[must_use]
pub fn newline_indent(text: &str, cursor: usize) -> (String, usize) {
let byte = char_to_byte(text, cursor);
let line_start = text[..byte].rfind('\n').map_or(0, |index| index + 1);
let indent: String = text[line_start..]
.chars()
.take_while(|c| *c == ' ' || *c == '\t')
.collect();
let mut insertion = String::with_capacity(indent.len() + 1);
insertion.push('\n');
insertion.push_str(&indent);
insert(text, cursor, &insertion)
}
#[must_use]
pub fn indent_line(text: &str, cursor: usize) -> (String, usize) {
let start = line_start_char(text, cursor);
let (result, _) = insert(text, start, INDENT);
(result, cursor + INDENT.chars().count())
}
#[must_use]
pub fn dedent_line(text: &str, cursor: usize) -> (String, usize) {
let start = line_start_char(text, cursor);
let start_byte = char_to_byte(text, start);
let removable = text[start_byte..]
.chars()
.take(INDENT.len())
.take_while(|c| *c == ' ')
.count();
if removable == 0 {
return (text.to_owned(), cursor);
}
let end_byte = char_to_byte(text, start + removable);
let mut result = text.to_owned();
result.replace_range(start_byte..end_byte, "");
let new_cursor = if cursor >= start + removable {
cursor - removable
} else {
start
};
(result, new_cursor)
}
#[must_use]
pub fn line_start_char(text: &str, cursor: usize) -> usize {
let byte = char_to_byte(text, cursor);
let start_byte = text[..byte].rfind('\n').map_or(0, |index| index + 1);
text[..start_byte].chars().count()
}
#[must_use]
pub fn line_end_char(text: &str, cursor: usize) -> usize {
let byte = char_to_byte(text, cursor);
let end_byte = text[byte..]
.find('\n')
.map_or(text.len(), |index| byte + index);
text[..end_byte].chars().count()
}