1use crate::completion::char_to_byte;
4
5const INDENT: &str = " ";
6
7#[must_use]
10pub fn insert(text: &str, cursor: usize, insertion: &str) -> (String, usize) {
11 let byte = char_to_byte(text, cursor);
12 let mut result = text.to_owned();
13 result.insert_str(byte, insertion);
14 (result, cursor + insertion.chars().count())
15}
16
17#[must_use]
20pub fn newline_indent(text: &str, cursor: usize) -> (String, usize) {
21 let byte = char_to_byte(text, cursor);
22 let line_start = text[..byte].rfind('\n').map_or(0, |index| index + 1);
23 let indent: String = text[line_start..]
24 .chars()
25 .take_while(|c| *c == ' ' || *c == '\t')
26 .collect();
27 let mut insertion = String::with_capacity(indent.len() + 1);
28 insertion.push('\n');
29 insertion.push_str(&indent);
30 insert(text, cursor, &insertion)
31}
32
33#[must_use]
35pub fn indent_line(text: &str, cursor: usize) -> (String, usize) {
36 let start = line_start_char(text, cursor);
37 let (result, _) = insert(text, start, INDENT);
38 (result, cursor + INDENT.chars().count())
39}
40
41#[must_use]
44pub fn dedent_line(text: &str, cursor: usize) -> (String, usize) {
45 let start = line_start_char(text, cursor);
46 let start_byte = char_to_byte(text, start);
47 let removable = text[start_byte..]
48 .chars()
49 .take(INDENT.len())
50 .take_while(|c| *c == ' ')
51 .count();
52 if removable == 0 {
53 return (text.to_owned(), cursor);
54 }
55 let end_byte = char_to_byte(text, start + removable);
56 let mut result = text.to_owned();
57 result.replace_range(start_byte..end_byte, "");
58 let new_cursor = if cursor >= start + removable {
59 cursor - removable
60 } else {
61 start
62 };
63 (result, new_cursor)
64}
65
66#[must_use]
72pub fn changed_range(before: &str, after: &str) -> (usize, usize) {
73 let before_len = before.chars().count();
74 let after_len = after.chars().count();
75 let prefix = before
76 .chars()
77 .zip(after.chars())
78 .take_while(|(old, new)| old == new)
79 .count();
80 let suffix = before
81 .chars()
82 .rev()
83 .zip(after.chars().rev())
84 .take_while(|(old, new)| old == new)
85 .count()
86 .min(before_len.min(after_len) - prefix);
87 (prefix, after_len - suffix)
88}
89
90#[must_use]
92pub fn line_start_char(text: &str, cursor: usize) -> usize {
93 let byte = char_to_byte(text, cursor);
94 let start_byte = text[..byte].rfind('\n').map_or(0, |index| index + 1);
95 text[..start_byte].chars().count()
96}
97
98#[must_use]
100pub fn line_end_char(text: &str, cursor: usize) -> usize {
101 let byte = char_to_byte(text, cursor);
102 let end_byte = text[byte..]
103 .find('\n')
104 .map_or(text.len(), |index| byte + index);
105 text[..end_byte].chars().count()
106}