1use super::main::Zle;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum TextObjectType {
12 Inner,
14 A,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum TextObjectKind {
21 Word,
22 BigWord,
23 Sentence,
24 Paragraph,
25 Parenthesis,
26 Bracket,
27 Brace,
28 Angle,
29 SingleQuote,
30 DoubleQuote,
31 BackQuote,
32}
33
34#[derive(Debug, Clone, Copy)]
36pub struct TextObject {
37 pub start: usize,
38 pub end: usize,
39}
40
41impl Zle {
42 pub fn select_text_object(
44 &self,
45 obj_type: TextObjectType,
46 kind: TextObjectKind,
47 ) -> Option<TextObject> {
48 match kind {
49 TextObjectKind::Word => self.select_word_object(obj_type, false),
50 TextObjectKind::BigWord => self.select_word_object(obj_type, true),
51 TextObjectKind::Sentence => self.select_sentence_object(obj_type),
52 TextObjectKind::Paragraph => self.select_paragraph_object(obj_type),
53 TextObjectKind::Parenthesis => self.select_pair_object(obj_type, '(', ')'),
54 TextObjectKind::Bracket => self.select_pair_object(obj_type, '[', ']'),
55 TextObjectKind::Brace => self.select_pair_object(obj_type, '{', '}'),
56 TextObjectKind::Angle => self.select_pair_object(obj_type, '<', '>'),
57 TextObjectKind::SingleQuote => self.select_quote_object(obj_type, '\''),
58 TextObjectKind::DoubleQuote => self.select_quote_object(obj_type, '"'),
59 TextObjectKind::BackQuote => self.select_quote_object(obj_type, '`'),
60 }
61 }
62
63 fn select_word_object(&self, obj_type: TextObjectType, big_word: bool) -> Option<TextObject> {
64 if self.zlell == 0 {
65 return None;
66 }
67
68 let is_word_char = if big_word {
69 |c: char| !c.is_whitespace()
70 } else {
71 |c: char| c.is_alphanumeric() || c == '_'
72 };
73
74 let mut start = self.zlecs;
75 let mut end = self.zlecs;
76
77 let on_word = if self.zlecs < self.zlell {
79 is_word_char(self.zleline[self.zlecs])
80 } else {
81 false
82 };
83
84 if on_word {
85 while start > 0 && is_word_char(self.zleline[start - 1]) {
87 start -= 1;
88 }
89 while end < self.zlell && is_word_char(self.zleline[end]) {
90 end += 1;
91 }
92
93 if obj_type == TextObjectType::A {
95 while end < self.zlell && self.zleline[end].is_whitespace() {
96 end += 1;
97 }
98 }
99 } else {
100 while start > 0 && self.zleline[start - 1].is_whitespace() {
102 start -= 1;
103 }
104 while end < self.zlell && self.zleline[end].is_whitespace() {
105 end += 1;
106 }
107
108 if obj_type == TextObjectType::A && end < self.zlell {
110 while end < self.zlell && is_word_char(self.zleline[end]) {
111 end += 1;
112 }
113 }
114 }
115
116 if start < end {
117 Some(TextObject { start, end })
118 } else {
119 None
120 }
121 }
122
123 fn select_sentence_object(&self, obj_type: TextObjectType) -> Option<TextObject> {
124 let mut start = self.zlecs;
126 let mut end = self.zlecs;
127
128 while start > 0 {
130 let c = self.zleline[start - 1];
131 if c == '.' || c == '!' || c == '?' {
132 break;
133 }
134 start -= 1;
135 }
136
137 if obj_type == TextObjectType::Inner {
139 while start < self.zlell && self.zleline[start].is_whitespace() {
140 start += 1;
141 }
142 }
143
144 while end < self.zlell {
146 let c = self.zleline[end];
147 end += 1;
148 if c == '.' || c == '!' || c == '?' {
149 break;
150 }
151 }
152
153 if obj_type == TextObjectType::A {
155 while end < self.zlell && self.zleline[end].is_whitespace() {
156 end += 1;
157 }
158 }
159
160 if start < end {
161 Some(TextObject { start, end })
162 } else {
163 None
164 }
165 }
166
167 fn select_paragraph_object(&self, obj_type: TextObjectType) -> Option<TextObject> {
168 let mut start = self.zlecs;
169 let mut end = self.zlecs;
170
171 while start > 0 {
173 if start >= 2 && self.zleline[start - 1] == '\n' && self.zleline[start - 2] == '\n' {
174 break;
175 }
176 start -= 1;
177 }
178
179 while end < self.zlell {
181 if end + 1 < self.zlell && self.zleline[end] == '\n' && self.zleline[end + 1] == '\n' {
182 if obj_type == TextObjectType::A {
183 end += 2;
184 }
185 break;
186 }
187 end += 1;
188 }
189
190 if start < end {
191 Some(TextObject { start, end })
192 } else {
193 None
194 }
195 }
196
197 fn select_pair_object(
198 &self,
199 obj_type: TextObjectType,
200 open: char,
201 close: char,
202 ) -> Option<TextObject> {
203 let mut depth = 0;
204 let mut start = None;
205 let mut end = None;
206
207 for i in (0..=self.zlecs).rev() {
209 let c = self.zleline[i];
210 if c == close {
211 depth += 1;
212 } else if c == open {
213 if depth == 0 {
214 start = Some(i);
215 break;
216 }
217 depth -= 1;
218 }
219 }
220
221 depth = 0;
223 for i in self.zlecs..self.zlell {
224 let c = self.zleline[i];
225 if c == open {
226 depth += 1;
227 } else if c == close {
228 if depth == 0 {
229 end = Some(i + 1);
230 break;
231 }
232 depth -= 1;
233 }
234 }
235
236 match (start, end) {
237 (Some(s), Some(e)) => {
238 if obj_type == TextObjectType::Inner {
239 Some(TextObject {
240 start: s + 1,
241 end: e - 1,
242 })
243 } else {
244 Some(TextObject { start: s, end: e })
245 }
246 }
247 _ => None,
248 }
249 }
250
251 fn select_quote_object(&self, obj_type: TextObjectType, quote: char) -> Option<TextObject> {
252 let mut start = None;
253 let mut end = None;
254
255 for i in (0..=self.zlecs).rev() {
257 if self.zleline[i] == quote {
258 start = Some(i);
259 break;
260 }
261 }
262
263 if let Some(s) = start {
265 for i in (s + 1)..self.zlell {
266 if self.zleline[i] == quote {
267 end = Some(i + 1);
268 break;
269 }
270 }
271 }
272
273 match (start, end) {
274 (Some(s), Some(e)) => {
275 if obj_type == TextObjectType::Inner {
276 Some(TextObject {
277 start: s + 1,
278 end: e - 1,
279 })
280 } else {
281 Some(TextObject { start: s, end: e })
282 }
283 }
284 _ => None,
285 }
286 }
287}