makepad_widget/
textcursor.rs

1use makepad_render::*;
2
3use crate::textbuffer::*;
4
5#[derive(Clone, Debug, PartialEq)]
6pub struct TextCursor {
7    pub head: usize,
8    pub tail: usize,
9    pub max: usize
10}
11
12impl TextCursor {
13    pub fn has_selection(&self) -> bool {
14        self.head != self.tail
15    }
16    
17    pub fn order(&self) -> (usize, usize) {
18        if self.head > self.tail {
19            (self.tail, self.head)
20        }
21        else {
22            (self.head, self.tail)
23        }
24    }
25    
26    pub fn clamp_range(&mut self, range: &Option<(usize, usize)>) {
27        if let Some((off, len)) = range {
28            if self.head >= self.tail {
29                if self.head < off + len {self.head = off + len}
30                if self.tail > *off {self.tail = *off}
31            }
32            else {
33                if self.tail < off + len {self.tail = off + len}
34                if self.head > *off {self.head = *off}
35            }
36        }
37    }
38    
39    
40    pub fn delta(&self, delta: isize) -> (usize, usize) {
41        let (start, end) = self.order();
42        (((start as isize) + delta) as usize, ((end as isize) + delta) as usize)
43    }
44    
45    pub fn collapse(&mut self, start: usize, end: usize, new_len: usize) -> isize {
46        self.head = start + new_len;
47        self.tail = self.head;
48        ((new_len as isize) - (end - start) as isize)
49    }
50    
51    
52    pub fn calc_max(&mut self, text_buffer: &TextBuffer, old: (TextPos, usize)) -> (TextPos, usize) {
53        let pos = text_buffer.offset_to_text_pos_next(self.head, old.0, old.1);
54        self.max = pos.col;
55        return (pos, self.head)
56    } 
57    
58    pub fn move_home(&mut self, text_buffer: &TextBuffer) {
59        let pos = text_buffer.offset_to_text_pos(self.head);
60        
61        // alright lets walk the line from the left till its no longer 9 or 32
62        for (index, ch) in text_buffer.lines[pos.row].iter().enumerate() {
63            if *ch != '\t' && *ch != ' ' {
64                self.head = text_buffer.text_pos_to_offset(TextPos {row: pos.row, col: index});
65                return
66            }
67        }
68    }
69    
70    pub fn move_end(&mut self, text_buffer: &TextBuffer) {
71        let pos = text_buffer.offset_to_text_pos(self.head);
72        // alright lets walk the line from the left till its no longer 9 or 32
73        self.head = text_buffer.text_pos_to_offset(TextPos {row: pos.row, col: text_buffer.lines[pos.row].len()});
74    }
75    
76    pub fn move_left(&mut self, char_count: usize, _text_buffer: &TextBuffer) {
77        if self.head >= char_count {
78            self.head -= char_count;
79        }
80        else {
81            self.head = 0;
82        }
83    }
84    
85    pub fn move_right(&mut self, char_count: usize, total_char_count: usize, _text_buffer: &TextBuffer) {
86        if self.head + char_count < total_char_count {
87            self.head += char_count;
88        }
89        else {
90            self.head = total_char_count;
91        }
92    }
93    
94    pub fn move_up(&mut self, line_count: usize, text_buffer: &TextBuffer) {
95        let pos = text_buffer.offset_to_text_pos(self.head);
96        if pos.row >= line_count {
97            self.head = text_buffer.text_pos_to_offset(TextPos {row: pos.row - line_count, col: self.max});
98        }
99        else {
100            self.head = 0;
101        }
102    }
103    
104    pub fn move_down(&mut self, line_count: usize, total_char_count: usize, text_buffer: &TextBuffer) {
105        let pos = text_buffer.offset_to_text_pos(self.head);
106        
107        if pos.row + line_count < text_buffer.get_line_count() - 1 {
108            
109            self.head = text_buffer.text_pos_to_offset(TextPos {row: pos.row + line_count, col: self.max});
110        }
111        else {
112            self.head = total_char_count;
113        }
114    }
115}
116
117#[derive(Clone)]
118pub struct TextCursorSet {
119    pub set: Vec<TextCursor>,
120    pub last_cursor: usize,
121    pub insert_undo_group: u64,
122    pub last_clamp_range: Option<(usize, usize)>
123}
124
125impl TextCursorSet {
126    pub fn new() -> TextCursorSet {
127        TextCursorSet {
128            set: vec![TextCursor {head: 0, tail: 0, max: 0}],
129            last_cursor: 0,
130            insert_undo_group: 0,
131            last_clamp_range: None
132        }
133    }
134    
135    pub fn get_all_as_string(&self, text_buffer: &TextBuffer) -> String {
136        let mut ret = String::new();
137        for cursor in &self.set {
138            let (start, end) = cursor.order();
139            text_buffer.get_range_as_string(start, end - start, &mut ret);
140        }
141        ret
142    }
143    
144    fn fuse_adjacent(&mut self, text_buffer: &TextBuffer) {
145        let mut index = 0;
146        let mut old_calc = (TextPos {row: 0, col: 0}, 0);
147        loop {
148            if self.set.len() < 2 || index >= self.set.len() - 1 { // no more pairs
149                return
150            }
151            // get the pair data
152            let (my_start, my_end) = self.set[index].order();
153            let (next_start, next_end) = self.set[index + 1].order();
154            if my_end >= next_start { // fuse them together
155                // check if we are mergin down or up
156                if my_end < next_end { // otherwise just remove the next
157                    if self.set[index].tail>self.set[index].head { // down
158                        self.set[index].head = my_start;
159                        self.set[index].tail = next_end;
160                    }
161                    else { // up
162                        self.set[index].head = next_end;
163                        self.set[index].tail = my_start;
164                    }
165                    old_calc = self.set[index].calc_max(text_buffer, old_calc);
166                    // remove the next item
167                }
168                if self.last_cursor > index {
169                    self.last_cursor -= 1;
170                }
171                self.set.remove(index + 1);
172            }
173            index += 1;
174        }
175    }
176    
177    fn remove_collisions(&mut self, offset: usize, len: usize) -> usize {
178        // remove any cursor that intersects us
179        let mut index = 0;
180        loop {
181            if index >= self.set.len() {
182                return index
183            }
184            let (start, end) = self.set[index].order();
185            if start > offset + len {
186                return index
187            }
188            if offset + len >= start && offset <= end {
189                self.set.remove(index);
190                // remove it
191            }
192            else {
193                index += 1;
194            }
195        }
196    }
197    
198    // puts the head down
199    fn set_last_cursor(&mut self, head: usize, tail: usize, text_buffer: &TextBuffer) {
200        // widen the head to include the min range
201        let (off, len) = if let Some((off, len)) = &self.last_clamp_range {
202            (*off, *len)
203        }
204        else {
205            (head, 0)
206        };
207        
208        // cut out all the collisions with the head range including 'last_cursor'
209        let index = self.remove_collisions(off, len);
210        
211        // make a new cursor
212        let mut cursor = TextCursor {
213            head: head,
214            tail: tail,
215            max: 0
216        };
217        
218        // clamp its head/tail to min range
219        cursor.clamp_range(&self.last_clamp_range);
220        // recompute maximum h pos
221        cursor.calc_max(text_buffer, (TextPos {row: 0, col: 0}, 0));
222        // insert it back into the set
223        self.set.insert(index, cursor);
224        self.last_cursor = index;
225    }
226    
227    pub fn add_last_cursor_head_and_tail(&mut self, offset: usize, text_buffer: &TextBuffer) {
228        self.insert_undo_group += 1;
229        // check if we dont already have exactly that cursor. ifso just remove it
230        if self.set.len()>1 {
231            for i in 0..self.set.len() {
232                if self.set[i].head == self.set[i].tail && self.set[i].head == offset {
233                    self.set.remove(i);
234                    self.last_cursor = self.set.len() - 1;
235                    return
236                }
237            }
238        }
239        self.set_last_cursor(offset, offset, text_buffer);
240    }
241    
242    pub fn clear_and_set_last_cursor_head_and_tail(&mut self, offset: usize, text_buffer: &TextBuffer) {
243        self.insert_undo_group += 1;
244        self.set.truncate(0);
245        self.set_last_cursor(offset, offset, text_buffer);
246    }
247    
248    pub fn set_last_cursor_head(&mut self, offset: usize, text_buffer: &TextBuffer) -> bool {
249        self.insert_undo_group += 1;
250        if self.set[self.last_cursor].head != offset {
251            let cursor_tail = self.set[self.last_cursor].tail;
252            self.set.remove(self.last_cursor);
253            self.set_last_cursor(offset, cursor_tail, text_buffer);
254            true
255        }
256        else {
257            false
258        }
259    }
260    
261    pub fn clear_and_set_last_cursor_head(&mut self, offset: usize, text_buffer: &TextBuffer) {
262        self.insert_undo_group += 1;
263        let cursor_tail = self.set[self.last_cursor].tail;
264        self.set.truncate(0);
265        self.set_last_cursor(offset, cursor_tail, text_buffer);
266    }
267    
268    pub fn get_last_cursor_text_pos(&self, text_buffer: &TextBuffer) -> TextPos {
269        text_buffer.offset_to_text_pos(self.set[self.last_cursor].head)
270    }
271    
272    pub fn get_last_cursor_order(&self) -> (usize, usize) {
273        self.set[self.last_cursor].order()
274    }
275    
276    pub fn get_last_cursor_singular(&self) -> Option<usize> {
277        let cursor = &self.set[self.last_cursor];
278        if cursor.head != cursor.tail {
279            None
280        }
281        else {
282            Some(cursor.head)
283        }
284    }
285    
286    pub fn is_last_cursor_singular(&self) -> bool {
287        let cursor = &self.set[self.last_cursor];
288        cursor.head == cursor.tail
289    }
290    
291    pub fn grid_select_corner(&mut self, new_pos: TextPos, text_buffer: &TextBuffer) -> TextPos {
292        self.insert_undo_group += 1;
293        // we need to compute the furthest row/col in our cursor set
294        let mut max_dist = 0.0;
295        let mut max_pos = TextPos {row: 0, col: 0};
296        for cursor in &self.set {
297            let head_pos = text_buffer.offset_to_text_pos(cursor.head);
298            let tail_pos = text_buffer.offset_to_text_pos(cursor.tail);
299            let head_dist = head_pos.dist(&new_pos);
300            let tail_dist = tail_pos.dist(&new_pos);
301            if head_dist > tail_dist {
302                if head_dist >= max_dist {
303                    max_dist = head_dist;
304                    max_pos = head_pos;
305                }
306            }
307            else {
308                if tail_dist >= max_dist {
309                    max_dist = tail_dist;
310                    max_pos = tail_pos;
311                }
312            }
313        }
314        return max_pos;
315    }
316    
317    pub fn grid_select(&mut self, start_pos: TextPos, end_pos: TextPos, text_buffer: &TextBuffer) -> bool {
318        self.insert_undo_group += 1;
319        let (left, right) = if start_pos.col < end_pos.col {(start_pos.col, end_pos.col)}
320        else {(end_pos.col, start_pos.col)};
321        
322        let (top, bottom) = if start_pos.row < end_pos.row {(start_pos.row, end_pos.row)}
323        else {(end_pos.row, start_pos.row)};
324        
325        let change_check = self.set.clone();
326        self.set.truncate(0);
327        // lets start the cursor gen
328        let mut offset = text_buffer.text_pos_to_offset(TextPos {row: top, col: 0});
329        for row in top..(bottom + 1) {
330            let line = &text_buffer.lines[row];
331            if left < line.len() {
332                if start_pos.col < end_pos.col {
333                    self.set.push(TextCursor {
334                        tail: offset + left,
335                        head: offset + line.len().min(right),
336                        max: line.len().min(right)
337                    });
338                }
339                else {
340                    self.set.push(TextCursor {
341                        head: offset + left,
342                        tail: offset + line.len().min(right),
343                        max: line.len().min(right)
344                    });
345                }
346            }
347            offset += line.len() + 1;
348        }
349        // depending on the direction the last cursor remains
350        if self.set.len() == 0{
351            let offset = text_buffer.text_pos_to_offset(end_pos);
352            self.set.push(TextCursor{
353                head:offset,
354                tail:offset,
355                max:0
356            })
357        }
358        self.last_cursor = 0;
359        self.set != change_check
360    }
361    
362    pub fn set_last_clamp_range(&mut self, range: (usize, usize)) {
363        self.last_clamp_range = Some(range);
364    }
365    
366    pub fn clear_last_clamp_range(&mut self) {
367        self.last_clamp_range = None;
368    }
369    
370    pub fn insert_newline_with_indent(&mut self, text_buffer: &mut TextBuffer) {
371        let mut delta: isize = 0;
372        // rolling delta to displace cursors
373        let mut ops = Vec::new();
374        let cursors_clone = self.clone();
375        let mut old_max = (TextPos {row: 0, col: 0}, 0);
376        for cursor in &mut self.set {
377            let (start, end) = cursor.delta(delta);
378            // lets find where we are as a cursor in the textbuffer
379            if start == end && start > 0 && start < text_buffer.flat_text.len() {
380                // insert spaces till indent level
381                let (pre_base, pre_spaces) = text_buffer.calc_next_line_indent_depth(start, 4);
382                
383                let pch = text_buffer.flat_text[start - 1];
384                let nch = text_buffer.flat_text[start];
385                // we have to insert more newlines and spaces because we were between () {} or []
386                if pch == '{' && nch == '}' || pch == '(' && nch == ')' || pch == '[' && nch == ']' {
387                    let mut text = String::new();
388                    text.push_str("\n");
389                    for _ in 0..pre_spaces {
390                        text.push_str(" ");
391                    }
392                    let post_spaces = pre_spaces.max(4) - 4;
393                    text.push_str("\n");
394                    for _ in 0..post_spaces {
395                        text.push_str(" ");
396                    };
397                    let op = text_buffer.replace_lines_with_string(start, end - start, &text);
398                    cursor.head += pre_spaces + 1;
399                    cursor.tail = cursor.head;
400                    delta += (pre_spaces + post_spaces + 2) as isize;
401                    ops.push(op);
402                }
403                else if pre_spaces != (start - pre_base) && nch == '}' || nch == ')' || nch == ']' { // deindent next one
404                    let mut text = String::new();
405                    text.push_str("\n");
406                    for _ in 0..(pre_spaces.max(4) - 4) {
407                        text.push_str(" ");
408                    }
409                    let op = text_buffer.replace_lines_with_string(start, end - start, &text);
410                    delta += cursor.collapse(start, end, op.len);
411                    ops.push(op);
412                }
413                else { // just do a newline with indenting
414                    let mut text = String::new();
415                    text.push_str("\n");
416                    for _ in 0..pre_spaces {
417                        text.push_str(" ");
418                    }
419                    let op = text_buffer.replace_lines_with_string(start, end - start, &text);
420                    delta += cursor.collapse(start, end, op.len);
421                    ops.push(op);
422                }
423            }
424            else {
425                let op = text_buffer.replace_lines_with_string(start, end - start, "\n");
426                delta += cursor.collapse(start, end, op.len);
427                ops.push(op);
428            };
429            
430            old_max = cursor.calc_max(text_buffer, old_max);
431        }
432        text_buffer.redo_stack.truncate(0);
433        text_buffer.undo_stack.push(TextUndo {
434            ops: ops,
435            grouping: TextUndoGrouping::Newline,
436            cursors: cursors_clone
437        })
438    }
439    
440    pub fn replace_text(&mut self, text: &str, text_buffer: &mut TextBuffer) {
441        let grouping = if text.len() == 1 {
442            // check if we are space
443            let ch = text.chars().next().unwrap();
444            if ch == ' ' {
445                TextUndoGrouping::Space
446            }
447            else if ch == '\n' {
448                TextUndoGrouping::Newline
449            }
450            else {
451                TextUndoGrouping::Character(self.insert_undo_group)
452            }
453        }
454        else if text.len() == 0 {
455            TextUndoGrouping::Cut
456        }
457        else {
458            TextUndoGrouping::Block
459        };
460        
461        let mut delta: isize = 0;
462        // rolling delta to displace cursors
463        let mut ops = Vec::new();
464        let mut old_max = (TextPos {row: 0, col: 0}, 0);
465        let cursors_clone = self.clone();
466        for cursor in &mut self.set {
467            let (start, end) = cursor.delta(delta);
468            let op = text_buffer.replace_lines_with_string(start, end - start, text);
469            delta += cursor.collapse(start, end, op.len);
470            ops.push(op);
471            old_max = cursor.calc_max(text_buffer, old_max);
472        }
473        text_buffer.redo_stack.truncate(0);
474        text_buffer.undo_stack.push(TextUndo {
475            ops: ops,
476            grouping: grouping,
477            cursors: cursors_clone
478        })
479    }
480    
481    pub fn insert_around(&mut self, pre: &str, post: &str, text_buffer: &mut TextBuffer) {
482        let mut delta: isize = 0;
483        // rolling delta to displace cursors
484        let mut ops = Vec::new();
485        let mut old_max = (TextPos {row: 0, col: 0}, 0);
486        let cursors_clone = self.clone();
487        for cursor in &mut self.set {
488            let (start, end) = cursor.delta(delta);
489            let pre_chars = pre.chars().count();
490            let post_chars = post.chars().count();
491            
492            // check if we should
493            if start != end {
494                // lets serialize our selection
495                let mut text = String::new();
496                text.push_str(pre);
497                text_buffer.get_range_as_string(start, end - start, &mut text);
498                text.push_str(post);
499                let op = text_buffer.replace_lines_with_string(start, end - start, &text);
500                // we wanna keep the original selection pushed by l
501                cursor.head += pre_chars + delta as usize;
502                cursor.tail += pre_chars + delta as usize;
503                delta += (pre_chars + post_chars) as isize;
504                ops.push(op);
505            }
506            else { // only insert post if next char is whitespace newline, : , or .
507                let ch = text_buffer.get_char(start);
508                if ch == ' ' || ch == ',' || ch == '.'
509                    || ch == ';' || ch == '\n' || ch == '\0'
510                    || ch == '}' || ch == ']' || ch == ')' {
511                    let mut text = String::new();
512                    text.push_str(pre);
513                    text.push_str(post);
514                    let op = text_buffer.replace_lines_with_string(start, 0, &text);
515                    cursor.head += pre_chars + delta as usize;
516                    cursor.tail += pre_chars + delta as usize;
517                    delta += (pre_chars + post_chars) as isize;
518                    ops.push(op);
519                }
520                else {
521                    let op = text_buffer.replace_lines_with_string(start, 0, pre);
522                    cursor.head += pre_chars + delta as usize;
523                    cursor.tail += pre_chars + delta as usize;
524                    delta += (pre_chars + post_chars) as isize;
525                    ops.push(op);
526                }
527            }
528            old_max = cursor.calc_max(text_buffer, old_max);
529        }
530        text_buffer.redo_stack.truncate(0);
531        text_buffer.undo_stack.push(TextUndo {
532            ops: ops,
533            grouping: TextUndoGrouping::Block,
534            cursors: cursors_clone
535        })
536    }
537    
538    pub fn overwrite_if_exists_or_deindent(&mut self, thing: &str, deindent: usize, text_buffer: &mut TextBuffer) {
539        let mut delta: isize = 0;
540        // rolling delta to displace cursors
541        let mut ops = Vec::new();
542        let mut old_max = (TextPos {row: 0, col: 0}, 0);
543        let cursors_clone = self.clone();
544        for cursor in &mut self.set {
545            let (start, end) = cursor.delta(delta);
546            // if start == end do overwrite if exists
547            let mut next = String::new();
548            let thing_chars = thing.chars().count();
549            text_buffer.get_range_as_string(start, thing_chars, &mut next);
550            
551            if start == end && thing == next {
552                // replace thing with next as an op even though its a noop
553                let op = text_buffer.replace_lines_with_string(start, thing_chars, thing);
554                delta += cursor.collapse(start, end, op.len);
555                ops.push(op);
556            }
557            else {
558                if start == end {
559                    let deindented = if let Some((rstart, l1ws, l1len)) = text_buffer.calc_deindent_whitespace(start) {
560                        if start - rstart == l1len && l1ws == l1len && l1ws >= deindent { // empty line, deindent
561                            let op = text_buffer.replace_lines_with_string(
562                                start - deindent,
563                                deindent,
564                                thing
565                            );
566                            delta += cursor.collapse(
567                                start - deindent,
568                                start - deindent,
569                                op.len
570                            );
571                            ops.push(op);
572                            true
573                        }
574                        else {false}
575                    }
576                    else {false};
577                    if !deindented {
578                        let op = text_buffer.replace_lines_with_string(start, 0, thing);
579                        delta += cursor.collapse(start, start, op.len);
580                        ops.push(op);
581                        
582                    }
583                }
584                else {
585                    let op = text_buffer.replace_lines_with_string(start, end - start, thing);
586                    delta += cursor.collapse(start, end, op.len);
587                    ops.push(op);
588                }
589            };
590            old_max = cursor.calc_max(text_buffer, old_max);
591        }
592        text_buffer.redo_stack.truncate(0);
593        text_buffer.undo_stack.push(TextUndo {
594            ops: ops,
595            grouping: TextUndoGrouping::Block,
596            cursors: cursors_clone
597        })
598    }
599    
600    pub fn delete(&mut self, text_buffer: &mut TextBuffer) {
601        let mut delta: isize = 0;
602        // rolling delta to displace cursors
603        let mut ops = Vec::new();
604        let cursors_clone = self.clone();
605        let mut old_max = (TextPos {row: 0, col: 0}, 0);
606        for cursor in &mut self.set {
607            let (start, end) = cursor.delta(delta);
608            if start == end {
609                let op = if let Some((rstart, l1ws, l1len, l2ws)) = text_buffer.calc_deletion_whitespace(start) {
610                    if l1ws == l1len { // we wanna delete a whole line
611                        delta -= l1len as isize + 1;
612                        cursor.head = rstart + l2ws;
613                        cursor.tail = cursor.head;
614                        text_buffer.replace_lines_with_string(rstart, l1len + 1, "")
615                    }
616                    else if start == rstart + l1len { // if start == rstart + l1len we are at the end of a line
617                        delta -= l2ws as isize + 1;
618                        text_buffer.replace_lines_with_string(start, l2ws + 1, "")
619                    }
620                    else {
621                        delta += cursor.collapse(start, start + 1, 0);
622                        text_buffer.replace_lines_with_string(start, 1, "")
623                    }
624                }
625                else {
626                    delta += cursor.collapse(start, start + 1, 0);
627                    text_buffer.replace_lines_with_string(start, 1, "")
628                };
629                if op.lines.len()>0 {
630                    ops.push(op);
631                }
632            }
633            else if start != end {
634                let op = text_buffer.replace_lines_with_string(start, end - start, "");
635                if op.lines.len()>0 {
636                    ops.push(op);
637                }
638                delta += cursor.collapse(start, end, 0);
639            }
640            old_max = cursor.calc_max(text_buffer, old_max);
641        }
642        let del_pos = self.set[self.last_cursor].head;
643        text_buffer.redo_stack.truncate(0);
644        if ops.len()>0 {
645            text_buffer.undo_stack.push(TextUndo {
646                ops: ops,
647                grouping: TextUndoGrouping::Delete(del_pos),
648                cursors: cursors_clone
649            })
650        }
651    }
652    
653    pub fn backspace(&mut self, text_buffer: &mut TextBuffer, undo_id: u64) {
654        let mut delta: isize = 0;
655        // rolling delta to displace cursors
656        let mut ops = Vec::new();
657        let cursors_clone = self.clone();
658        let mut old_max = (TextPos {row: 0, col: 0}, 0);
659        for cursor in &mut self.set {
660            let (start, end) = cursor.delta(delta);
661            if start == end && start > 0 {
662                // check our indent depth
663                let (new_start, new_len) = text_buffer.calc_backspace_line_indent_depth_and_pair(start);
664                let op = text_buffer.replace_lines_with_string(new_start, new_len, "");
665                if op.lines.len()>0 {
666                    ops.push(op);
667                }
668                delta += cursor.collapse(new_start, new_start + new_len, 0);
669                // so what if following is newline, indent, )]}
670            }
671            else if start != end {
672                let op = text_buffer.replace_lines_with_string(start, end - start, "");
673                if op.lines.len()>0 {
674                    ops.push(op);
675                }
676                delta += cursor.collapse(start, end, 0);
677            }
678            old_max = cursor.calc_max(text_buffer, old_max);
679        }
680        text_buffer.redo_stack.truncate(0);
681        if ops.len()>0 {
682            text_buffer.undo_stack.push(TextUndo {
683                ops: ops,
684                grouping: TextUndoGrouping::Backspace(undo_id),
685                cursors: cursors_clone
686            })
687        }
688    }
689    
690    pub fn insert_tab(&mut self, text_buffer: &mut TextBuffer, tab_str: &str) {
691        let mut delta: usize = 0;
692        // rolling delta to displace cursors
693        let mut ops = Vec::new();
694        let tab_str_chars = tab_str.chars().count();
695        let cursors_clone = self.clone();
696        let mut old_max = (TextPos {row: 0, col: 0}, 0);
697        for cursor in &mut self.set {
698            let (start, end) = cursor.delta(delta as isize);
699            /*
700            i find these things really bad UX. so lets not.
701            if start == end{ // just insert 4 spaces
702            // check our indent depth
703            let op = text_buffer.replace_lines_with_string(start, end-start, tab_str);
704            delta += cursor.collapse(start, end, op.len);
705            ops.push(op);
706            }
707            else if start != end{ // either indent the lines, OR replace
708            let start_pos = text_buffer.offset_to_text_pos_next(start, old_max.0, old_max.1);
709            let end_pos = text_buffer.offset_to_text_pos_next(end, start_pos, start);
710            if start_pos.row == end_pos.row{ // its a single line replace with 4 chars
711            let op = text_buffer.replace_lines_with_string(start, end - start, tab_str);
712            ops.push(op);
713            delta += cursor.collapse(start, end, tab_str_chars);
714            }
715            else{ // tab indent the lines
716            */
717            let start_pos = text_buffer.offset_to_text_pos_next(start, old_max.0, old_max.1);
718            let end_pos = text_buffer.offset_to_text_pos_next(end, start_pos, start);
719            let mut off = start - start_pos.col;
720            let last_line = if start_pos.row == end_pos.row || end_pos.col>0 {1}else {0};
721            for row in start_pos.row..(end_pos.row + last_line) {
722                // ok so how do we compute the actual op offset of this line
723                let op = text_buffer.replace_line_with_string(off, row, 0, 0, tab_str);
724                off += text_buffer.lines[row].len() + 1;
725                ops.push(op);
726            }
727            // figure out which way the cursor is
728            if cursor.head > cursor.tail {
729                cursor.tail += tab_str_chars + delta;
730                cursor.head += (end_pos.row - start_pos.row + last_line) * tab_str_chars + delta;
731            }
732            else {
733                cursor.tail += (end_pos.row - start_pos.row + last_line) * tab_str_chars + delta;
734                cursor.head += tab_str_chars + delta;
735            }
736            delta += ((end_pos.row - start_pos.row) + 1) * tab_str_chars;
737            // }
738            //}
739            old_max = cursor.calc_max(text_buffer, old_max);
740        }
741        text_buffer.redo_stack.truncate(0);
742        text_buffer.undo_stack.push(TextUndo {
743            ops: ops,
744            grouping: TextUndoGrouping::Tab,
745            cursors: cursors_clone
746        })
747    }
748    
749    pub fn replace_lines_formatted(&mut self, mut out_lines: Vec<Vec<char>>, text_buffer: &mut TextBuffer) {
750        
751        let mut top_row = 0;
752        while top_row < text_buffer.lines.len() && top_row < out_lines.len() && text_buffer.lines[top_row] == out_lines[top_row] {
753            top_row += 1;
754        }
755        
756        let mut bottom_row_old = text_buffer.get_line_count();
757        let mut bottom_row_new = out_lines.len();
758        while bottom_row_old > top_row && bottom_row_new > top_row && text_buffer.lines[bottom_row_old - 1] == out_lines[bottom_row_new - 1] {
759            bottom_row_old -= 1;
760            bottom_row_new -= 1;
761        }
762        // alright we now have a line range to replace.
763        if top_row != bottom_row_new {
764            let changed = out_lines.splice(top_row..(bottom_row_new + 1).min(out_lines.len()), vec![]).collect();
765            
766            let cursors_clone = self.clone();
767            let op = text_buffer.replace_lines(top_row, bottom_row_old + 1, changed);
768            text_buffer.redo_stack.truncate(0);
769            text_buffer.undo_stack.push(TextUndo {
770                ops: vec![op],
771                grouping: TextUndoGrouping::Format,
772                cursors: cursors_clone
773            })
774        }
775    }
776    /*
777    pub fn toggle_comment(&mut self, text_buffer:&mut TextBuffer, comment_str:&str){
778        let mut delta:usize = 0; // rolling delta to displace cursors
779        let mut ops = Vec::new();
780        let comment_str_chars = comment_str.chars().count();
781        let cursors_clone = self.clone();
782        let mut old_max = (TextPos{row:0,col:0},0);
783        for cursor in &mut self.set{
784        let (start, end) = cursor.delta(delta as isize);
785
786        let start_pos = text_buffer.offset_to_text_pos_next(start, old_max.0, old_max.1);
787        let end_pos = text_buffer.offset_to_text_pos_next(end, start_pos, start);
788        let mut off = start - start_pos.col;
789        let last_line = if start_pos.row == end_pos.row || end_pos.col>0{1}else{0};
790
791        for row in start_pos.row..(end_pos.row+last_line){
792        // ok so how do we compute the actual op offset of this line
793        let op = text_buffer.replace_line_with_string(off, row, 0, 0, tab_str);
794        off += text_buffer.lines[row].len() + 1;
795        ops.push(op);
796        }
797        // figure out which way the cursor is
798        if cursor.head > cursor.tail{
799        cursor.tail += tab_str_chars + delta;
800        cursor.head += (end_pos.row - start_pos.row + last_line) * tab_str_chars + delta;
801        }
802        else{
803        cursor.tail += (end_pos.row - start_pos.row + last_line) * tab_str_chars + delta;
804        cursor.head += tab_str_chars + delta;
805        }
806        delta += ((end_pos.row - start_pos.row) + 1) * tab_str_chars;
807        old_max = cursor.calc_max(text_buffer, old_max);
808        }
809        text_buffer.redo_stack.truncate(0);
810        text_buffer.undo_stack.push(TextUndo{
811        ops:ops,
812        grouping:TextUndoGrouping::Tab,
813        cursors:cursors_clone
814        })
815    }*/
816    
817    pub fn remove_tab(&mut self, text_buffer: &mut TextBuffer, num_spaces: usize) {
818        
819        let mut delta: usize = 0;
820        // rolling delta to displace cursors
821        let mut ops = Vec::new();
822        let cursors_clone = self.clone();
823        let mut old_max = (TextPos {row: 0, col: 0}, 0);
824        for cursor in &mut self.set {
825            let (start, end) = cursor.delta(-(delta as isize));
826            let start_pos = text_buffer.offset_to_text_pos_next(start, old_max.0, old_max.1);
827            let end_pos = text_buffer.offset_to_text_pos_next(end, start_pos, start);
828            let mut off = start - start_pos.col;
829            let mut total_cut_len = 0;
830            
831            let last_line = if start_pos.row == end_pos.row || end_pos.col>0 {1}else {0};
832            for row in start_pos.row..(end_pos.row + last_line) {
833                let indents = text_buffer.calc_line_indent_depth(row);
834                let cut_len = num_spaces.min(indents);
835                if cut_len > 0 {
836                    total_cut_len += cut_len;
837                    let op = text_buffer.replace_line_with_string(off, row, 0, num_spaces, "");
838                    if cursor.head > off {
839                        cursor.head -= cut_len;
840                    }
841                    if cursor.tail > off {
842                        cursor.tail -= cut_len;
843                    }
844                    ops.push(op);
845                }
846                off += text_buffer.lines[row].len() + 1;
847            }
848            cursor.head -= delta;
849            cursor.tail -= delta;
850            delta += total_cut_len;
851            old_max = cursor.calc_max(text_buffer, old_max);
852        }
853        text_buffer.redo_stack.truncate(0);
854        text_buffer.undo_stack.push(TextUndo {
855            ops: ops,
856            grouping: TextUndoGrouping::Tab,
857            cursors: cursors_clone
858        })
859        
860    }
861    
862    
863    pub fn select_all(&mut self, text_buffer: &mut TextBuffer) {
864        self.set.truncate(0);
865        self.insert_undo_group += 1;
866        let mut cursor = TextCursor {
867            head: 0,
868            tail: text_buffer.calc_char_count(),
869            max: 0
870        };
871        self.last_cursor = 0;
872        cursor.calc_max(text_buffer, (TextPos {row: 0, col: 0}, 0));
873        self.set.push(cursor);
874    }
875    
876    pub fn move_home(&mut self, only_head: bool, text_buffer: &TextBuffer) {
877        self.insert_undo_group += 1;
878        for cursor in &mut self.set {
879            cursor.move_home(text_buffer);
880            if !only_head {cursor.tail = cursor.head}
881        }
882        self.fuse_adjacent(text_buffer)
883    }
884    
885    pub fn move_end(&mut self, only_head: bool, text_buffer: &TextBuffer) {
886        self.insert_undo_group += 1;
887        for cursor in &mut self.set {
888            cursor.move_end(text_buffer);
889            if !only_head {cursor.tail = cursor.head}
890        }
891        self.fuse_adjacent(text_buffer)
892    }
893    
894    pub fn move_up(&mut self, line_count: usize, only_head: bool, text_buffer: &TextBuffer) {
895        self.insert_undo_group += 1;
896        for cursor in &mut self.set {
897            cursor.move_up(line_count, text_buffer);
898            if !only_head {cursor.tail = cursor.head}
899        }
900        self.fuse_adjacent(text_buffer)
901    }
902    
903    pub fn move_down(&mut self, line_count: usize, only_head: bool, text_buffer: &TextBuffer) {
904        self.insert_undo_group += 1;
905        let total_char_count = text_buffer.calc_char_count();
906        for cursor in &mut self.set {
907            cursor.move_down(line_count, total_char_count, text_buffer);
908            if !only_head {cursor.tail = cursor.head}
909        }
910        self.fuse_adjacent(text_buffer)
911    }
912    
913    pub fn move_left(&mut self, char_count: usize, only_head: bool, text_buffer: &TextBuffer) {
914        self.insert_undo_group += 1;
915        let mut old_max = (TextPos {row: 0, col: 0}, 0);
916        for cursor in &mut self.set {
917            if cursor.head != cursor.tail && !only_head {
918                cursor.head = cursor.head.min(cursor.tail)
919            }
920            else {
921                cursor.move_left(char_count, text_buffer);
922            }
923            if !only_head {cursor.tail = cursor.head}
924            old_max = cursor.calc_max(text_buffer, old_max);
925        }
926        self.fuse_adjacent(text_buffer)
927    }
928    
929    pub fn move_right(&mut self, char_count: usize, only_head: bool, text_buffer: &TextBuffer) {
930        let mut old_max = (TextPos {row: 0, col: 0}, 0);
931        let total_char_count = text_buffer.calc_char_count();
932        for cursor in &mut self.set {
933            if cursor.head != cursor.tail && !only_head {
934                cursor.head = cursor.head.max(cursor.tail)
935            }
936            else {
937                cursor.move_right(char_count, total_char_count, text_buffer);
938            }
939            if !only_head {cursor.tail = cursor.head}
940            old_max = cursor.calc_max(text_buffer, old_max);
941        }
942        self.fuse_adjacent(text_buffer)
943    }
944    
945    pub fn get_nearest_token_chunk_boundary(left: bool, offset: usize, text_buffer: &TextBuffer) -> usize {
946        let token_chunks = &text_buffer.token_chunks;
947        for i in 0..token_chunks.len() {
948            // if we are in the chunk, decide what to do
949            if offset >= token_chunks[i].offset && offset < token_chunks[i].offset + token_chunks[i].len {
950                if left { // we want to to the beginning of the prev token
951                    if offset > token_chunks[i].offset {
952                        return token_chunks[i].offset
953                    }
954                    if i == 0 {
955                        return 0
956                    }
957                    if offset == token_chunks[i].offset {
958                        if token_chunks[i - 1].token_type == TokenType::Whitespace && i>1 {
959                            return token_chunks[i - 2].offset // + chunks[i-2].len
960                        }
961                        return token_chunks[i - 1].offset
962                    }
963                    return token_chunks[i - 1].offset + token_chunks[i - 1].len
964                }
965                else { // jump right
966                    
967                    if i < token_chunks.len() - 1 && token_chunks[i].token_type == TokenType::Whitespace {
968                        return token_chunks[i + 1].offset + token_chunks[i + 1].len;
969                    }
970                    return token_chunks[i].offset + token_chunks[i].len
971                }
972            }
973        };
974        0
975    }
976    
977    pub fn get_nearest_token_chunk(offset: usize, text_buffer: &TextBuffer) -> Option<(usize, usize)> {
978        let token_chunks = &text_buffer.token_chunks;
979        for i in 0..token_chunks.len() {
980            if token_chunks[i].token_type == TokenType::Whitespace {
981                if offset == token_chunks[i].offset && i > 0 { // at the start of whitespace
982                    return Some((token_chunks[i - 1].offset, token_chunks[i - 1].len))
983                }
984                else if offset == token_chunks[i].offset + token_chunks[i].len && i < token_chunks.len() - 1 {
985                    return Some((token_chunks[i + 1].offset, token_chunks[i + 1].len))
986                }
987            };
988            
989            if offset >= token_chunks[i].offset && offset < token_chunks[i].offset + token_chunks[i].len {
990                let i = if token_chunks[i].token_type == TokenType::Newline && i > 0 {i - 1}else {i};
991                let pair_token = token_chunks[i].pair_token;
992                if pair_token > i {
993                    return Some((token_chunks[i].offset, token_chunks[pair_token].len + (token_chunks[pair_token].offset - token_chunks[i].offset)));
994                }
995                if token_chunks[i].token_type == TokenType::String || token_chunks[i].token_type == TokenType::CommentChunk {
996                    if token_chunks[i].len <= 2 {
997                        return Some((token_chunks[i].offset, token_chunks[i].len));
998                    }
999                    else { // scan for the nearest left and right space in the string
1000                        let mut scan_left = offset;
1001                        let boundary_tokens = "' :(){}[]+-|/<,.>;\"'!%^&*=";
1002                        while scan_left > 0 && scan_left > token_chunks[i].offset {
1003                            if let Some(_) = boundary_tokens.find(text_buffer.flat_text[scan_left]) {
1004                                scan_left += 1;
1005                                break
1006                            }
1007                            scan_left -= 1;
1008                        }
1009                        if let Some(_) = boundary_tokens.find(text_buffer.flat_text[scan_left]) {
1010                            scan_left += 1;
1011                        }
1012                        let mut scan_right = offset;
1013                        while scan_right < token_chunks[i].offset + token_chunks[i].len {
1014                            if let Some(_) = boundary_tokens.find(text_buffer.flat_text[scan_right]) {
1015                                //scan_left += 1;
1016                                break
1017                            }
1018                            scan_right += 1;
1019                        }
1020                        if scan_right <= scan_left {
1021                            return Some((token_chunks[i].offset, token_chunks[i].len))
1022                        }
1023                        else {
1024                            return Some((scan_left, scan_right - scan_left))
1025                        }
1026                    }
1027                }
1028                return Some((token_chunks[i].offset, token_chunks[i].len));
1029            }
1030        };
1031        None
1032    }
1033    
1034    pub fn move_left_nearest_token(&mut self, only_head: bool, text_buffer: &TextBuffer) {
1035        self.insert_undo_group += 1;
1036        for cursor in &mut self.set {
1037            // take the cursor head and find nearest token left
1038            let pos = TextCursorSet::get_nearest_token_chunk_boundary(true, cursor.head, text_buffer);
1039            cursor.head = pos;
1040            if !only_head {cursor.tail = cursor.head}
1041        }
1042        self.fuse_adjacent(text_buffer)
1043    }
1044    
1045    pub fn move_right_nearest_token(&mut self, only_head: bool, text_buffer: &TextBuffer) {
1046        self.insert_undo_group += 1;
1047        for cursor in &mut self.set {
1048            // take the cursor head and find nearest token left
1049            let pos = TextCursorSet::get_nearest_token_chunk_boundary(false, cursor.head, text_buffer);
1050            cursor.head = pos;
1051            if !only_head {cursor.tail = cursor.head}
1052        }
1053        self.fuse_adjacent(text_buffer)
1054    }
1055    
1056    pub fn get_token_highlight(&self, text_buffer: &TextBuffer) -> Vec<char> {
1057        let cursor = &self.set[self.last_cursor];
1058        if cursor.head != cursor.tail {
1059            return vec![]
1060        }
1061        if let Some((offset, len)) = TextCursorSet::get_nearest_token_chunk(cursor.head, text_buffer) {
1062            /*
1063            let add = match chunk.token_type {
1064                TokenType::Whitespace => false,
1065                TokenType::Newline => false,
1066                TokenType::Keyword => false,
1067                TokenType::BuiltinType => false,
1068                TokenType::Flow => false,
1069                TokenType::Fn => false,
1070                TokenType::TypeDef => false,
1071                TokenType::Looping => false,
1072                TokenType::Identifier => true,
1073                TokenType::Call => true,
1074                TokenType::TypeName => true,
1075                TokenType::Bool => true,
1076                TokenType::String => true,
1077                TokenType::Regex => true,
1078                TokenType::Number => true,
1079                TokenType::CommentLine => false,
1080                TokenType::CommentMultiBegin => false,
1081                TokenType::CommentMultiEnd => false,
1082                TokenType::CommentChunk => false,
1083                TokenType::ParenOpen => false,
1084                TokenType::ParenClose => false,
1085                TokenType::Operator => false,
1086                TokenType::Splat => false,
1087                TokenType::Colon => false,
1088                TokenType::Namespace => false,
1089                TokenType::Hash => false,
1090                TokenType::Delimiter => false,
1091                TokenType::Unexpected => false,
1092                TokenType::Eof => false,
1093            };
1094            if !add {
1095                vec![]
1096            }*/
1097            //else {
1098            let start_pos = text_buffer.offset_to_text_pos(offset);
1099            
1100            text_buffer.copy_line(start_pos.row, start_pos.col, len)
1101            //}
1102        }
1103        else {
1104            vec![]
1105        }
1106    }
1107    
1108    pub fn get_selection_highlight(&self, text_buffer: &TextBuffer) -> Vec<char> {
1109        let cursor = &self.set[self.last_cursor];
1110        if cursor.head != cursor.tail {
1111            let (start, end) = cursor.order();
1112            let start_pos = text_buffer.offset_to_text_pos(start);
1113            let end_pos = text_buffer.offset_to_text_pos_next(end, start_pos, start);
1114            if start_pos.row != end_pos.row || end_pos.col <= start_pos.col {
1115                return vec![]
1116            };
1117            let buf = text_buffer.copy_line(start_pos.row, start_pos.col, end_pos.col - start_pos.col);
1118            let mut only_spaces = true;
1119            for ch in &buf {
1120                if *ch != ' ' {
1121                    only_spaces = false;
1122                    break;
1123                }
1124            };
1125            if only_spaces {
1126                return vec![]
1127            }
1128            return buf
1129        }
1130        else {
1131            return vec![]
1132        }
1133    }
1134}
1135
1136#[derive(Clone)]
1137pub struct DrawSel {
1138    pub index: usize,
1139    pub rc: Rect,
1140}
1141
1142#[derive(Clone)]
1143pub struct DrawCursors {
1144    pub head: usize,
1145    pub start: usize,
1146    pub end: usize,
1147    pub next_index: usize,
1148    pub left_top: Vec2,
1149    pub right_bottom: Vec2,
1150    pub last_w: f32,
1151    pub first: bool,
1152    pub empty: bool,
1153    pub cursors: Vec<CursorRect>,
1154    pub last_cursor: Option<usize>,
1155    pub selections: Vec<DrawSel>
1156}
1157
1158#[derive(Clone, Copy)]
1159pub struct CursorRect {
1160    pub x: f32,
1161    pub y: f32,
1162    pub w: f32,
1163    pub h: f32,
1164    pub z: f32
1165}
1166
1167impl DrawCursors {
1168    pub fn new() -> DrawCursors {
1169        DrawCursors {
1170            start: 0,
1171            end: 0,
1172            head: 0,
1173            first: true,
1174            empty: true,
1175            next_index: 0,
1176            left_top: Vec2::default(),
1177            right_bottom: Vec2::default(),
1178            last_w: 0.0,
1179            cursors: Vec::new(),
1180            selections: Vec::new(),
1181            last_cursor: None
1182        }
1183    }
1184    
1185    pub fn term(&mut self, cursors: &Vec<TextCursor>) {
1186        self.next_index = cursors.len();
1187    }
1188    
1189    pub fn set_next(&mut self, cursors: &Vec<TextCursor>) -> bool {
1190        if self.next_index < cursors.len() {
1191            self.emit_selection();
1192            let cursor = &cursors[self.next_index];
1193            let (start, end) = cursor.order();
1194            self.start = start;
1195            self.end = end;
1196            self.head = cursor.head;
1197            self.next_index += 1;
1198            self.last_w = 0.0;
1199            self.right_bottom.y = 0.;
1200            self.first = true;
1201            self.empty = true;
1202            true
1203        }
1204        else {
1205            false
1206        }
1207    }
1208    
1209    pub fn emit_cursor(&mut self, x: f32, y: f32, h: f32, z: f32) {
1210        self.cursors.push(CursorRect {
1211            x: x,
1212            y: y,
1213            w: 1.5,
1214            h: h,
1215            z: z
1216        })
1217    }
1218    
1219    pub fn emit_selection(&mut self) {
1220        if !self.first {
1221            self.first = true;
1222            if !self.empty {
1223                self.selections.push(DrawSel {
1224                    index: self.next_index - 1,
1225                    rc: Rect {
1226                        x: self.left_top.x,
1227                        y: self.left_top.y,
1228                        w: (self.right_bottom.x - self.left_top.x),
1229                        h: self.right_bottom.y - self.left_top.y
1230                    }
1231                })
1232            }
1233            self.right_bottom.y = 0.;
1234        }
1235    }
1236    
1237    pub fn emit_selection_new_line(&mut self) {
1238        if !self.first {
1239            self.first = true;
1240            self.selections.push(DrawSel {
1241                index: self.next_index - 1,
1242                rc: Rect {
1243                    x: self.left_top.x,
1244                    y: self.left_top.y,
1245                    w: (self.right_bottom.x - self.left_top.x) + self.last_w,
1246                    h: self.right_bottom.y - self.left_top.y
1247                }
1248            });
1249            self.right_bottom.y = 0.;
1250        }
1251    }
1252    
1253    pub fn process_cursor(&mut self, last_cursor: usize, offset: usize, x: f32, y: f32, h: f32, z: f32) {
1254        if offset == self.head { // emit a cursor
1255            if self.next_index > 0 && self.next_index - 1 == last_cursor {
1256                self.last_cursor = Some(self.cursors.len());
1257            }
1258            self.emit_cursor(x, y, h, z);
1259        }
1260    }
1261    
1262    pub fn process_geom(&mut self, x: f32, y: f32, w: f32, h: f32) {
1263        if self.first { // store left top of rect
1264            self.first = false;
1265            self.left_top.x = x;
1266            self.left_top.y = y;
1267            self.empty = true;
1268        }
1269        else {
1270            self.empty = false;
1271        }
1272        // current right/bottom
1273        self.last_w = w;
1274        self.right_bottom.x = x;
1275        if y + h > self.right_bottom.y {
1276            self.right_bottom.y = y + h;
1277        }
1278    }
1279    
1280    pub fn process_newline(&mut self) {
1281        if !self.first { // we have some selection data to emit
1282            self.emit_selection_new_line();
1283            self.first = true;
1284        }
1285    }
1286    
1287    pub fn mark_text_select_only(&mut self, cursors: &Vec<TextCursor>, offset: usize, x: f32, y: f32, w: f32, h: f32) {
1288        // check if we need to skip cursors
1289        while offset >= self.end { // jump to next cursor
1290            if offset == self.end { // process the last bit here
1291                self.process_geom(x, y, w, h);
1292                self.emit_selection();
1293            }
1294            if !self.set_next(cursors) { // cant go further
1295                return
1296            }
1297        }
1298        // in current cursor range, update values
1299        if offset >= self.start && offset <= self.end {
1300            self.process_geom(x, y, w, h);
1301            if offset == self.end {
1302                self.emit_selection();
1303            }
1304        }
1305    }
1306    
1307    pub fn mark_text_with_cursor(&mut self, cursors: &Vec<TextCursor>, ch: char, offset: usize, x: f32, y: f32, w: f32, h: f32, z: f32, last_cursor: usize, mark_spaces: f32) -> f32 {
1308        // check if we need to skip cursors
1309        while offset >= self.end { // jump to next cursor
1310            if offset == self.end { // process the last bit here
1311                self.process_cursor(last_cursor, offset, x, y, h, z);
1312                self.process_geom(x, y, w, h);
1313                self.emit_selection();
1314            }
1315            if !self.set_next(cursors) { // cant go further
1316                return mark_spaces
1317            }
1318        }
1319        // in current cursor range, update values
1320        if offset >= self.start && offset <= self.end {
1321            self.process_cursor(last_cursor, offset, x, y, h, z);
1322            self.process_geom(x, y, w, h);
1323            if offset == self.end {
1324                self.emit_selection();
1325            }
1326            if ch == '\n' {
1327                return 0.0
1328            }
1329            else if ch == ' ' && offset < self.end {
1330                return 2.0
1331            }
1332        }
1333        return mark_spaces
1334    }
1335}