use zaplib::*;
use crate::textbuffer::*;
use crate::tokentype::*;
#[derive(Clone, Debug, PartialEq)]
pub struct TextCursor {
pub head: usize,
pub tail: usize,
pub max: usize,
}
impl TextCursor {
pub fn has_selection(&self) -> bool {
self.head != self.tail
}
pub fn order(&self) -> (usize, usize) {
if self.head > self.tail {
(self.tail, self.head)
} else {
(self.head, self.tail)
}
}
pub fn clamp_range(&mut self, range: &Option<(usize, usize)>) {
if let Some((off, len)) = range {
if self.head >= self.tail {
if self.head < off + len {
self.head = off + len
}
if self.tail > *off {
self.tail = *off
}
} else {
if self.tail < off + len {
self.tail = off + len
}
if self.head > *off {
self.head = *off
}
}
}
}
pub fn delta(&self, delta: isize) -> (usize, usize) {
let (start, end) = self.order();
(((start as isize) + delta) as usize, ((end as isize) + delta) as usize)
}
pub fn collapse(&mut self, start: usize, end: usize, new_len: usize) -> isize {
self.head = start + new_len;
self.tail = self.head;
(new_len as isize) - (end - start) as isize
}
pub fn calc_max(&mut self, text_buffer: &TextBuffer, old: (TextPos, usize)) -> (TextPos, usize) {
let pos = text_buffer.offset_to_text_pos_next(self.head, old.0, old.1);
self.max = pos.col;
(pos, self.head)
}
pub fn move_home(&mut self, text_buffer: &TextBuffer) {
let pos = text_buffer.offset_to_text_pos(self.head);
for (index, ch) in text_buffer.lines[pos.row].iter().enumerate() {
if *ch != '\t' && *ch != ' ' {
self.head = text_buffer.text_pos_to_offset(TextPos { row: pos.row, col: index });
return;
}
}
}
pub fn move_end(&mut self, text_buffer: &TextBuffer) {
let pos = text_buffer.offset_to_text_pos(self.head);
self.head = text_buffer.text_pos_to_offset(TextPos { row: pos.row, col: text_buffer.lines[pos.row].len() });
}
pub fn move_left(&mut self, char_count: usize, _text_buffer: &TextBuffer) {
if self.head >= char_count {
self.head -= char_count;
} else {
self.head = 0;
}
}
pub fn move_right(&mut self, char_count: usize, total_char_count: usize, _text_buffer: &TextBuffer) {
if self.head + char_count < total_char_count {
self.head += char_count;
} else {
self.head = total_char_count;
}
}
pub fn move_up(&mut self, line_count: usize, text_buffer: &TextBuffer) {
let pos = text_buffer.offset_to_text_pos(self.head);
if pos.row >= line_count {
self.head = text_buffer.text_pos_to_offset(TextPos { row: pos.row - line_count, col: self.max });
} else {
self.head = 0;
}
}
pub fn move_down(&mut self, line_count: usize, total_char_count: usize, text_buffer: &TextBuffer) {
let pos = text_buffer.offset_to_text_pos(self.head);
if pos.row + line_count < text_buffer.get_line_count() - 1 {
self.head = text_buffer.text_pos_to_offset(TextPos { row: pos.row + line_count, col: self.max });
} else {
self.head = total_char_count;
}
}
}
#[derive(Clone)]
pub struct TextCursorSet {
pub set: Vec<TextCursor>,
pub last_cursor: usize,
pub insert_undo_group: u64,
pub last_clamp_range: Option<(usize, usize)>,
}
impl Default for TextCursorSet {
fn default() -> Self {
Self { set: vec![TextCursor { head: 0, tail: 0, max: 0 }], last_cursor: 0, insert_undo_group: 0, last_clamp_range: None }
}
}
impl TextCursorSet {
pub fn get_all_as_string(&self, text_buffer: &TextBuffer) -> String {
let mut ret = String::new();
for cursor in &self.set {
let (start, end) = cursor.order();
text_buffer.get_range_as_string(start, end - start, &mut ret);
}
ret
}
pub fn get_ident_around_last_cursor_and_set(&mut self, text_buffer: &TextBuffer) -> String {
let mut ret = String::new();
let cursor = &mut self.set[self.last_cursor];
if cursor.head != cursor.tail {
let (start, end) = cursor.order();
text_buffer.get_range_as_string(start, end - start, &mut ret);
let mut exact = false;
for tok in &text_buffer.token_chunks {
if start == tok.offset && end == tok.offset + tok.len {
exact = true;
break;
}
}
if !exact {
ret.push('*');
}
return ret;
}
for tok in &text_buffer.token_chunks {
if cursor.head >= tok.offset && cursor.head <= tok.offset + tok.len {
match &tok.token_type {
TokenType::Identifier | TokenType::Call | TokenType::TypeName => {
text_buffer.get_range_as_string(tok.offset, tok.len, &mut ret);
cursor.tail = tok.offset;
cursor.head = tok.offset + tok.len;
self.fuse_adjacent(text_buffer);
return ret;
}
_ => (),
}
}
}
ret
}
fn fuse_adjacent(&mut self, text_buffer: &TextBuffer) {
let mut index = 0;
let mut old_calc = (TextPos { row: 0, col: 0 }, 0);
loop {
if self.set.len() < 2 || index >= self.set.len() - 1 {
return;
}
let (my_start, my_end) = self.set[index].order();
let (next_start, next_end) = self.set[index + 1].order();
if my_end >= next_start {
if my_end < next_end {
if self.set[index].tail > self.set[index].head {
self.set[index].head = my_start;
self.set[index].tail = next_end;
} else {
self.set[index].head = next_end;
self.set[index].tail = my_start;
}
old_calc = self.set[index].calc_max(text_buffer, old_calc);
}
if self.last_cursor > index {
self.last_cursor -= 1;
}
self.set.remove(index + 1);
}
index += 1;
}
}
fn remove_collisions(&mut self, offset: usize, len: usize) -> usize {
let mut index = 0;
loop {
if index >= self.set.len() {
return index;
}
let (start, end) = self.set[index].order();
if start > offset + len {
return index;
}
if offset + len >= start && offset <= end {
self.set.remove(index);
} else {
index += 1;
}
}
}
fn set_last_cursor(&mut self, head: usize, tail: usize, text_buffer: &TextBuffer) {
let (off, len) = if let Some((off, len)) = &self.last_clamp_range { (*off, *len) } else { (head, 0) };
let index = self.remove_collisions(off, len);
let mut cursor = TextCursor { head, tail, max: 0 };
cursor.clamp_range(&self.last_clamp_range);
cursor.calc_max(text_buffer, (TextPos { row: 0, col: 0 }, 0));
self.set.insert(index, cursor);
self.last_cursor = index;
}
pub fn add_last_cursor_head_and_tail(&mut self, head: usize, tail: usize, text_buffer: &TextBuffer) {
self.insert_undo_group += 1;
if self.set.len() > 1 {
for i in 0..self.set.len() {
if self.set[i].head == self.set[i].tail && self.set[i].head == head {
self.set.remove(i);
self.last_cursor = self.set.len() - 1;
return;
}
}
}
self.set_last_cursor(head, tail, text_buffer);
}
pub fn clear_and_set_last_cursor_head_and_tail(&mut self, head: usize, tail: usize, text_buffer: &TextBuffer) {
self.insert_undo_group += 1;
self.set.truncate(0);
self.set_last_cursor(head, tail, text_buffer);
}
pub fn set_last_cursor_head_and_tail(&mut self, head: usize, tail: usize, text_buffer: &TextBuffer) {
self.insert_undo_group += 1;
self.set.remove(self.last_cursor);
self.set_last_cursor(head, tail, text_buffer);
}
pub fn set_last_cursor_head(&mut self, offset: usize, text_buffer: &TextBuffer) -> bool {
self.insert_undo_group += 1;
if self.set[self.last_cursor].head != offset {
let cursor_tail = self.set[self.last_cursor].tail;
self.set.remove(self.last_cursor);
self.set_last_cursor(offset, cursor_tail, text_buffer);
true
} else {
false
}
}
pub fn clear_and_set_last_cursor_head(&mut self, offset: usize, text_buffer: &TextBuffer) {
self.insert_undo_group += 1;
let cursor_tail = self.set[self.last_cursor].tail;
self.set.truncate(0);
self.set_last_cursor(offset, cursor_tail, text_buffer);
}
pub fn get_last_cursor_text_pos(&self, text_buffer: &TextBuffer) -> TextPos {
text_buffer.offset_to_text_pos(self.set[self.last_cursor].head)
}
pub fn get_last_cursor_order(&self) -> (usize, usize) {
self.set[self.last_cursor].order()
}
pub fn get_last_cursor_head(&self) -> usize {
self.set[self.last_cursor].head
}
pub fn get_last_cursor_singular(&self) -> Option<usize> {
let cursor = &self.set[self.last_cursor];
if cursor.head != cursor.tail {
None
} else {
Some(cursor.head)
}
}
pub fn is_last_cursor_singular(&self) -> bool {
let cursor = &self.set[self.last_cursor];
cursor.head == cursor.tail
}
pub fn grid_select_corner(&mut self, new_pos: TextPos, text_buffer: &TextBuffer) -> TextPos {
self.insert_undo_group += 1;
let mut max_dist = 0.0;
let mut max_pos = TextPos { row: 0, col: 0 };
for cursor in &self.set {
let head_pos = text_buffer.offset_to_text_pos(cursor.head);
let tail_pos = text_buffer.offset_to_text_pos(cursor.tail);
let head_dist = head_pos.dist(&new_pos);
let tail_dist = tail_pos.dist(&new_pos);
if head_dist > tail_dist {
if head_dist >= max_dist {
max_dist = head_dist;
max_pos = head_pos;
}
} else if tail_dist >= max_dist {
max_dist = tail_dist;
max_pos = tail_pos;
}
}
max_pos
}
pub fn grid_select(&mut self, start_pos: TextPos, end_pos: TextPos, text_buffer: &TextBuffer) -> bool {
self.insert_undo_group += 1;
let (left, right) = if start_pos.col < end_pos.col { (start_pos.col, end_pos.col) } else { (end_pos.col, start_pos.col) };
let (top, bottom) = if start_pos.row < end_pos.row { (start_pos.row, end_pos.row) } else { (end_pos.row, start_pos.row) };
let change_check = self.set.clone();
self.set.truncate(0);
let mut offset = text_buffer.text_pos_to_offset(TextPos { row: top, col: 0 });
for row in top..(bottom + 1) {
let line = &text_buffer.lines[row];
if left < line.len() {
if start_pos.col < end_pos.col {
self.set.push(TextCursor {
tail: offset + left,
head: offset + line.len().min(right),
max: line.len().min(right),
});
} else {
self.set.push(TextCursor {
head: offset + left,
tail: offset + line.len().min(right),
max: line.len().min(right),
});
}
}
offset += line.len() + 1;
}
if self.set.is_empty() {
let offset = text_buffer.text_pos_to_offset(end_pos);
self.set.push(TextCursor { head: offset, tail: offset, max: 0 })
}
self.last_cursor = 0;
self.set != change_check
}
pub fn set_last_clamp_range(&mut self, range: (usize, usize)) {
self.last_clamp_range = Some(range);
}
pub fn clear_last_clamp_range(&mut self) {
self.last_clamp_range = None;
}
pub fn insert_newline_with_indent(&mut self, text_buffer: &mut TextBuffer) {
let mut delta: isize = 0;
let mut ops = Vec::new();
let cursors_clone = self.clone();
let mut old_max = (TextPos { row: 0, col: 0 }, 0);
for cursor in &mut self.set {
let (start, end) = cursor.delta(delta);
if start == end && start > 0 && start < text_buffer.flat_text.len() {
let (pre_base, pre_spaces) = text_buffer.calc_next_line_indent_depth(start, 4);
let pch = text_buffer.flat_text[start - 1];
let nch = text_buffer.flat_text[start];
if pch == '{' && nch == '}' || pch == '(' && nch == ')' || pch == '[' && nch == ']' {
let mut text = String::new();
text.push('\n');
for _ in 0..pre_spaces {
text.push(' ');
}
let post_spaces = pre_spaces.max(4) - 4;
text.push('\n');
for _ in 0..post_spaces {
text.push(' ');
}
let op = text_buffer.replace_lines_with_string(start, end - start, &text);
cursor.head += pre_spaces + 1;
cursor.tail = cursor.head;
delta += (pre_spaces + post_spaces + 2) as isize;
ops.push(op);
} else if pre_spaces != (start - pre_base) && nch == '}' || nch == ')' || nch == ']' {
let mut text = String::new();
text.push('\n');
for _ in 0..(pre_spaces.max(4) - 4) {
text.push(' ');
}
let op = text_buffer.replace_lines_with_string(start, end - start, &text);
delta += cursor.collapse(start, end, op.len);
ops.push(op);
} else {
let mut text = String::new();
text.push('\n');
for _ in 0..pre_spaces {
text.push(' ');
}
let op = text_buffer.replace_lines_with_string(start, end - start, &text);
delta += cursor.collapse(start, end, op.len);
ops.push(op);
}
} else {
let op = text_buffer.replace_lines_with_string(start, end - start, "\n");
delta += cursor.collapse(start, end, op.len);
ops.push(op);
};
old_max = cursor.calc_max(text_buffer, old_max);
}
text_buffer.redo_stack.truncate(0);
text_buffer.undo_stack.push(TextUndo { ops, grouping: TextUndoGrouping::Newline, cursors: cursors_clone })
}
pub fn replace_text(&mut self, text: &str, text_buffer: &mut TextBuffer, override_group: Option<TextUndoGrouping>) {
let grouping = if let Some(override_group) = override_group {
override_group
} else if text.len() == 1 {
let ch = text.chars().next().unwrap();
if ch == ' ' {
TextUndoGrouping::Space
} else if ch == '\n' {
TextUndoGrouping::Newline
} else {
TextUndoGrouping::Character(self.insert_undo_group)
}
} else if text.is_empty() {
TextUndoGrouping::Cut
} else {
TextUndoGrouping::Block
};
let mut delta: isize = 0;
let mut ops = Vec::new();
let mut old_max = (TextPos { row: 0, col: 0 }, 0);
let cursors_clone = self.clone();
for cursor in &mut self.set {
let (start, end) = cursor.delta(delta);
let op = text_buffer.replace_lines_with_string(start, end - start, text);
delta += cursor.collapse(start, end, op.len);
ops.push(op);
old_max = cursor.calc_max(text_buffer, old_max);
}
text_buffer.redo_stack.truncate(0);
text_buffer.undo_stack.push(TextUndo { ops, grouping, cursors: cursors_clone })
}
pub fn insert_around(&mut self, pre: &str, post: &str, text_buffer: &mut TextBuffer) {
let mut delta: isize = 0;
let mut ops = Vec::new();
let mut old_max = (TextPos { row: 0, col: 0 }, 0);
let cursors_clone = self.clone();
for cursor in &mut self.set {
let (start, end) = cursor.delta(delta);
let pre_chars = pre.chars().count();
let post_chars = post.chars().count();
if start != end {
let mut text = String::new();
text.push_str(pre);
text_buffer.get_range_as_string(start, end - start, &mut text);
text.push_str(post);
let op = text_buffer.replace_lines_with_string(start, end - start, &text);
cursor.head += pre_chars + delta as usize;
cursor.tail += pre_chars + delta as usize;
delta += (pre_chars + post_chars) as isize;
ops.push(op);
} else {
let ch = text_buffer.get_char(start);
if ch == ' '
|| ch == ','
|| ch == '.'
|| ch == ';'
|| ch == '\n'
|| ch == '\0'
|| ch == '}'
|| ch == ']'
|| ch == ')'
{
let mut text = String::new();
text.push_str(pre);
text.push_str(post);
let op = text_buffer.replace_lines_with_string(start, 0, &text);
cursor.head += pre_chars + delta as usize;
cursor.tail += pre_chars + delta as usize;
delta += (pre_chars + post_chars) as isize;
ops.push(op);
} else {
let op = text_buffer.replace_lines_with_string(start, 0, pre);
cursor.head += pre_chars + delta as usize;
cursor.tail += pre_chars + delta as usize;
delta += (pre_chars + post_chars) as isize;
ops.push(op);
}
}
old_max = cursor.calc_max(text_buffer, old_max);
}
text_buffer.redo_stack.truncate(0);
text_buffer.undo_stack.push(TextUndo { ops, grouping: TextUndoGrouping::Block, cursors: cursors_clone })
}
pub fn overwrite_if_exists_or_deindent(&mut self, thing: &str, deindent: usize, text_buffer: &mut TextBuffer) {
let mut delta: isize = 0;
let mut ops = Vec::new();
let mut old_max = (TextPos { row: 0, col: 0 }, 0);
let cursors_clone = self.clone();
for cursor in &mut self.set {
let (start, end) = cursor.delta(delta);
let mut next = String::new();
let thing_chars = thing.chars().count();
text_buffer.get_range_as_string(start, thing_chars, &mut next);
if start == end && thing == next {
let op = text_buffer.replace_lines_with_string(start, thing_chars, thing);
delta += cursor.collapse(start, end, op.len);
ops.push(op);
} else if start == end {
let deindented = if let Some((rstart, l1ws, l1len)) = text_buffer.calc_deindent_whitespace(start) {
if start - rstart == l1len && l1ws == l1len && l1ws >= deindent {
let op = text_buffer.replace_lines_with_string(start - deindent, deindent, thing);
delta += cursor.collapse(start - deindent, start - deindent, op.len);
ops.push(op);
true
} else {
false
}
} else {
false
};
if !deindented {
let op = text_buffer.replace_lines_with_string(start, 0, thing);
delta += cursor.collapse(start, start, op.len);
ops.push(op);
}
} else {
let op = text_buffer.replace_lines_with_string(start, end - start, thing);
delta += cursor.collapse(start, end, op.len);
ops.push(op);
};
old_max = cursor.calc_max(text_buffer, old_max);
}
text_buffer.redo_stack.truncate(0);
text_buffer.undo_stack.push(TextUndo { ops, grouping: TextUndoGrouping::Block, cursors: cursors_clone })
}
pub fn delete(&mut self, text_buffer: &mut TextBuffer) {
let mut delta: isize = 0;
let mut ops = Vec::new();
let cursors_clone = self.clone();
let mut old_max = (TextPos { row: 0, col: 0 }, 0);
for cursor in &mut self.set {
let (start, end) = cursor.delta(delta);
if start == end {
let op = if let Some((rstart, l1ws, l1len, l2ws)) = text_buffer.calc_deletion_whitespace(start) {
if l1ws == l1len {
delta -= l1len as isize + 1;
cursor.head = rstart + l2ws;
cursor.tail = cursor.head;
text_buffer.replace_lines_with_string(rstart, l1len + 1, "")
} else if start == rstart + l1len {
delta -= l2ws as isize + 1;
text_buffer.replace_lines_with_string(start, l2ws + 1, "")
} else {
delta += cursor.collapse(start, start + 1, 0);
text_buffer.replace_lines_with_string(start, 1, "")
}
} else {
delta += cursor.collapse(start, start + 1, 0);
text_buffer.replace_lines_with_string(start, 1, "")
};
if !op.lines.is_empty() {
ops.push(op);
}
} else if start != end {
let op = text_buffer.replace_lines_with_string(start, end - start, "");
if !op.lines.is_empty() {
ops.push(op);
}
delta += cursor.collapse(start, end, 0);
}
old_max = cursor.calc_max(text_buffer, old_max);
}
let del_pos = self.set[self.last_cursor].head;
text_buffer.redo_stack.truncate(0);
if !ops.is_empty() {
text_buffer.undo_stack.push(TextUndo { ops, grouping: TextUndoGrouping::Delete(del_pos), cursors: cursors_clone })
}
}
pub fn backspace(&mut self, text_buffer: &mut TextBuffer, undo_id: u64) {
let mut delta: isize = 0;
let mut ops = Vec::new();
let cursors_clone = self.clone();
let mut old_max = (TextPos { row: 0, col: 0 }, 0);
for cursor in &mut self.set {
let (start, end) = cursor.delta(delta);
if start == end && start > 0 {
let (new_start, new_len) = text_buffer.calc_backspace_line_indent_depth_and_pair(start);
let op = text_buffer.replace_lines_with_string(new_start, new_len, "");
if !op.lines.is_empty() {
ops.push(op);
}
delta += cursor.collapse(new_start, new_start + new_len, 0);
} else if start != end {
let op = text_buffer.replace_lines_with_string(start, end - start, "");
if !op.lines.is_empty() {
ops.push(op);
}
delta += cursor.collapse(start, end, 0);
}
old_max = cursor.calc_max(text_buffer, old_max);
}
text_buffer.redo_stack.truncate(0);
if !ops.is_empty() {
text_buffer.undo_stack.push(TextUndo { ops, grouping: TextUndoGrouping::Backspace(undo_id), cursors: cursors_clone })
}
}
pub fn insert_tab(&mut self, text_buffer: &mut TextBuffer, tab_str: &str) {
let mut delta: usize = 0;
let mut ops = Vec::new();
let tab_str_chars = tab_str.chars().count();
let cursors_clone = self.clone();
let mut old_max = (TextPos { row: 0, col: 0 }, 0);
for cursor in &mut self.set {
let (start, end) = cursor.delta(delta as isize);
let start_pos = text_buffer.offset_to_text_pos_next(start, old_max.0, old_max.1);
let end_pos = text_buffer.offset_to_text_pos_next(end, start_pos, start);
let mut off = start - start_pos.col;
let last_line = if start_pos.row == end_pos.row || end_pos.col > 0 { 1 } else { 0 };
for row in start_pos.row..(end_pos.row + last_line) {
let op = text_buffer.replace_line_with_string(off, row, 0, 0, tab_str);
off += text_buffer.lines[row].len() + 1;
ops.push(op);
}
if cursor.head > cursor.tail {
cursor.tail += tab_str_chars + delta;
cursor.head += (end_pos.row - start_pos.row + last_line) * tab_str_chars + delta;
} else {
cursor.tail += (end_pos.row - start_pos.row + last_line) * tab_str_chars + delta;
cursor.head += tab_str_chars + delta;
}
delta += ((end_pos.row - start_pos.row) + 1) * tab_str_chars;
old_max = cursor.calc_max(text_buffer, old_max);
}
text_buffer.redo_stack.truncate(0);
text_buffer.undo_stack.push(TextUndo { ops, grouping: TextUndoGrouping::Tab, cursors: cursors_clone })
}
pub fn replace_lines_formatted(&mut self, mut out_lines: Vec<Vec<char>>, text_buffer: &mut TextBuffer) {
let mut top_row = 0;
while top_row < text_buffer.lines.len() && top_row < out_lines.len() && text_buffer.lines[top_row] == out_lines[top_row] {
top_row += 1;
}
let mut bottom_row_old = text_buffer.get_line_count();
let mut bottom_row_new = out_lines.len();
while bottom_row_old > top_row
&& bottom_row_new > top_row
&& text_buffer.lines[bottom_row_old - 1] == out_lines[bottom_row_new - 1]
{
bottom_row_old -= 1;
bottom_row_new -= 1;
}
if top_row != bottom_row_new {
let changed = out_lines.splice(top_row..(bottom_row_new + 1).min(out_lines.len()), vec![]).collect();
let cursors_clone = self.clone();
let op = text_buffer.replace_lines(top_row, bottom_row_old + 1, changed);
text_buffer.redo_stack.truncate(0);
text_buffer.undo_stack.push(TextUndo { ops: vec![op], grouping: TextUndoGrouping::Format, cursors: cursors_clone })
}
}
pub fn remove_tab(&mut self, text_buffer: &mut TextBuffer, num_spaces: usize) {
let mut delta: usize = 0;
let mut ops = Vec::new();
let cursors_clone = self.clone();
let mut old_max = (TextPos { row: 0, col: 0 }, 0);
for cursor in &mut self.set {
let (start, end) = cursor.delta(-(delta as isize));
let start_pos = text_buffer.offset_to_text_pos_next(start, old_max.0, old_max.1);
let end_pos = text_buffer.offset_to_text_pos_next(end, start_pos, start);
let mut off = start - start_pos.col;
let mut total_cut_len = 0;
let last_line = if start_pos.row == end_pos.row || end_pos.col > 0 { 1 } else { 0 };
for row in start_pos.row..(end_pos.row + last_line) {
let indents = text_buffer.calc_line_indent_depth(row);
let cut_len = num_spaces.min(indents);
if cut_len > 0 {
total_cut_len += cut_len;
let op = text_buffer.replace_line_with_string(off, row, 0, num_spaces, "");
if cursor.head > off {
cursor.head -= cut_len;
}
if cursor.tail > off {
cursor.tail -= cut_len;
}
ops.push(op);
}
off += text_buffer.lines[row].len() + 1;
}
cursor.head -= delta;
cursor.tail -= delta;
delta += total_cut_len;
old_max = cursor.calc_max(text_buffer, old_max);
}
text_buffer.redo_stack.truncate(0);
text_buffer.undo_stack.push(TextUndo { ops, grouping: TextUndoGrouping::Tab, cursors: cursors_clone })
}
pub fn select_all(&mut self, text_buffer: &mut TextBuffer) {
self.set.truncate(0);
self.insert_undo_group += 1;
let mut cursor = TextCursor { head: text_buffer.calc_char_count(), tail: 0, max: 0 };
self.last_cursor = 0;
cursor.calc_max(text_buffer, (TextPos { row: 0, col: 0 }, 0));
self.set.push(cursor);
}
pub fn clamp_to_text_buffer(&mut self, text_buffer: &TextBuffer) {
let total_char_count = text_buffer.calc_char_count();
for cursor in &mut self.set {
cursor.head = cursor.head.min(total_char_count);
cursor.tail = cursor.tail.min(total_char_count);
}
self.fuse_adjacent(text_buffer)
}
pub fn move_home(&mut self, only_head: bool, text_buffer: &TextBuffer) {
self.insert_undo_group += 1;
for cursor in &mut self.set {
cursor.move_home(text_buffer);
if !only_head {
cursor.tail = cursor.head
}
}
self.fuse_adjacent(text_buffer)
}
pub fn move_end(&mut self, only_head: bool, text_buffer: &TextBuffer) {
self.insert_undo_group += 1;
for cursor in &mut self.set {
cursor.move_end(text_buffer);
if !only_head {
cursor.tail = cursor.head
}
}
self.fuse_adjacent(text_buffer)
}
pub fn move_up(&mut self, line_count: usize, only_head: bool, text_buffer: &TextBuffer) {
self.insert_undo_group += 1;
for cursor in &mut self.set {
cursor.move_up(line_count, text_buffer);
if !only_head {
cursor.tail = cursor.head
}
}
self.fuse_adjacent(text_buffer)
}
pub fn move_down(&mut self, line_count: usize, only_head: bool, text_buffer: &TextBuffer) {
self.insert_undo_group += 1;
let total_char_count = text_buffer.calc_char_count();
for cursor in &mut self.set {
cursor.move_down(line_count, total_char_count, text_buffer);
if !only_head {
cursor.tail = cursor.head
}
}
self.fuse_adjacent(text_buffer)
}
pub fn move_left(&mut self, char_count: usize, only_head: bool, text_buffer: &TextBuffer) {
self.insert_undo_group += 1;
let mut old_max = (TextPos { row: 0, col: 0 }, 0);
for cursor in &mut self.set {
if cursor.head != cursor.tail && !only_head {
cursor.head = cursor.head.min(cursor.tail)
} else {
cursor.move_left(char_count, text_buffer);
}
if !only_head {
cursor.tail = cursor.head
}
old_max = cursor.calc_max(text_buffer, old_max);
}
self.fuse_adjacent(text_buffer)
}
pub fn move_right(&mut self, char_count: usize, only_head: bool, text_buffer: &TextBuffer) {
let mut old_max = (TextPos { row: 0, col: 0 }, 0);
let total_char_count = text_buffer.calc_char_count();
for cursor in &mut self.set {
if cursor.head != cursor.tail && !only_head {
cursor.head = cursor.head.max(cursor.tail)
} else {
cursor.move_right(char_count, total_char_count, text_buffer);
}
if !only_head {
cursor.tail = cursor.head
}
old_max = cursor.calc_max(text_buffer, old_max);
}
self.fuse_adjacent(text_buffer)
}
pub fn get_nearest_token_chunk_boundary(left: bool, offset: usize, text_buffer: &TextBuffer) -> usize {
let token_chunks = &text_buffer.token_chunks;
for i in 0..token_chunks.len() {
if offset >= token_chunks[i].offset && offset < token_chunks[i].offset + token_chunks[i].len {
if left {
if offset > token_chunks[i].offset {
return token_chunks[i].offset;
}
if i == 0 {
return 0;
}
if offset == token_chunks[i].offset {
if token_chunks[i - 1].token_type == TokenType::Whitespace && i > 1 {
return token_chunks[i - 2].offset; }
return token_chunks[i - 1].offset;
}
return token_chunks[i - 1].offset + token_chunks[i - 1].len;
} else {
if i < token_chunks.len() - 1 && token_chunks[i].token_type == TokenType::Whitespace {
return token_chunks[i + 1].offset + token_chunks[i + 1].len;
}
return token_chunks[i].offset + token_chunks[i].len;
}
}
}
0
}
pub fn get_nearest_token_chunk(offset: usize, text_buffer: &TextBuffer) -> Option<(usize, usize)> {
let token_chunks = &text_buffer.token_chunks;
for i in 0..token_chunks.len() {
if token_chunks[i].token_type == TokenType::Whitespace {
if offset == token_chunks[i].offset && i > 0 {
return Some((token_chunks[i - 1].offset, token_chunks[i - 1].len));
} else if offset == token_chunks[i].offset + token_chunks[i].len && i < token_chunks.len() - 1 {
return Some((token_chunks[i + 1].offset, token_chunks[i + 1].len));
}
};
if offset >= token_chunks[i].offset && offset < token_chunks[i].offset + token_chunks[i].len {
let i = if token_chunks[i].token_type == TokenType::Newline && i > 0 { i - 1 } else { i };
let pair_token = token_chunks[i].pair_token;
if pair_token > i {
return Some((
token_chunks[i].offset,
token_chunks[pair_token].len + (token_chunks[pair_token].offset - token_chunks[i].offset),
));
}
if token_chunks[i].token_type == TokenType::String || token_chunks[i].token_type == TokenType::CommentChunk {
if token_chunks[i].len <= 2 {
return Some((token_chunks[i].offset, token_chunks[i].len));
} else {
let mut scan_left = offset;
let boundary_tokens = "' :(){}[]+-|/<,.>;\"'!%^&*=";
while scan_left > 0 && scan_left > token_chunks[i].offset {
if boundary_tokens.find(text_buffer.flat_text[scan_left]).is_some() {
scan_left += 1;
break;
}
scan_left -= 1;
}
if boundary_tokens.find(text_buffer.flat_text[scan_left]).is_some() {
scan_left += 1;
}
let mut scan_right = offset;
while scan_right < token_chunks[i].offset + token_chunks[i].len {
if boundary_tokens.find(text_buffer.flat_text[scan_right]).is_some() {
break;
}
scan_right += 1;
}
if scan_right <= scan_left {
return Some((token_chunks[i].offset, token_chunks[i].len));
} else {
return Some((scan_left, scan_right - scan_left));
}
}
}
return Some((token_chunks[i].offset, token_chunks[i].len));
}
}
None
}
pub fn move_left_nearest_token(&mut self, only_head: bool, text_buffer: &TextBuffer) {
self.insert_undo_group += 1;
for cursor in &mut self.set {
let pos = TextCursorSet::get_nearest_token_chunk_boundary(true, cursor.head, text_buffer);
cursor.head = pos;
if !only_head {
cursor.tail = cursor.head
}
}
self.fuse_adjacent(text_buffer)
}
pub fn move_right_nearest_token(&mut self, only_head: bool, text_buffer: &TextBuffer) {
self.insert_undo_group += 1;
for cursor in &mut self.set {
let pos = TextCursorSet::get_nearest_token_chunk_boundary(false, cursor.head, text_buffer);
cursor.head = pos;
if !only_head {
cursor.tail = cursor.head
}
}
self.fuse_adjacent(text_buffer)
}
pub fn get_token_highlight(&self, text_buffer: &TextBuffer) -> Vec<char> {
let cursor = &self.set[self.last_cursor];
if cursor.head != cursor.tail {
return vec![];
}
if let Some((offset, len)) = TextCursorSet::get_nearest_token_chunk(cursor.head, text_buffer) {
let start_pos = text_buffer.offset_to_text_pos(offset);
text_buffer.copy_line(start_pos.row, start_pos.col, len)
} else {
vec![]
}
}
pub fn get_selection_highlight(&self, text_buffer: &TextBuffer) -> Vec<char> {
let cursor = &self.set[self.last_cursor];
if cursor.head != cursor.tail {
let (start, end) = cursor.order();
let start_pos = text_buffer.offset_to_text_pos(start);
let end_pos = text_buffer.offset_to_text_pos_next(end, start_pos, start);
if start_pos.row != end_pos.row || end_pos.col <= start_pos.col {
return vec![];
};
let buf = text_buffer.copy_line(start_pos.row, start_pos.col, end_pos.col - start_pos.col);
let mut only_spaces = true;
for ch in &buf {
if *ch != ' ' {
only_spaces = false;
break;
}
}
if only_spaces {
return vec![];
}
buf
} else {
return vec![];
}
}
}
#[derive(Clone)]
pub struct DrawSel {
pub index: usize,
pub rc: Rect,
}
#[derive(Clone)]
pub struct DrawCursors {
pub head: usize,
pub start: usize,
pub end: usize,
pub next_index: usize,
pub left_top: Vec2,
pub right_bottom: Vec2,
pub last_w: f32,
pub first: bool,
pub empty: bool,
pub cursors: Vec<CursorRect>,
pub last_cursor: Option<usize>,
pub selections: Vec<DrawSel>,
}
#[derive(Clone, Copy)]
pub struct CursorRect {
pub x: f32,
pub y: f32,
pub w: f32,
pub h: f32,
pub z: f32,
}
impl Default for DrawCursors {
fn default() -> Self {
Self {
start: 0,
end: 0,
head: 0,
first: true,
empty: true,
next_index: 0,
left_top: Vec2::default(),
right_bottom: Vec2::default(),
last_w: 0.0,
cursors: Vec::new(),
selections: Vec::new(),
last_cursor: None,
}
}
}
impl DrawCursors {
pub fn term(&mut self, cursors: &[TextCursor]) {
self.next_index = cursors.len();
}
pub fn set_next(&mut self, cursors: &[TextCursor]) -> bool {
if self.next_index < cursors.len() {
self.emit_selection();
let cursor = &cursors[self.next_index];
let (start, end) = cursor.order();
self.start = start;
self.end = end;
self.head = cursor.head;
self.next_index += 1;
self.last_w = 0.0;
self.right_bottom.y = 0.;
self.first = true;
self.empty = true;
true
} else {
false
}
}
pub fn emit_cursor(&mut self, x: f32, y: f32, h: f32, z: f32) {
self.cursors.push(CursorRect { x, y, w: 1.5, h, z })
}
pub fn emit_selection(&mut self) {
if !self.first {
self.first = true;
if !self.empty {
self.selections.push(DrawSel {
index: self.next_index - 1,
rc: Rect { pos: self.left_top, size: self.right_bottom - self.left_top },
})
}
self.right_bottom.y = 0.;
}
}
pub fn emit_selection_new_line(&mut self) {
if !self.first {
self.first = true;
self.selections.push(DrawSel {
index: self.next_index - 1,
rc: Rect { pos: self.left_top, size: (self.right_bottom - self.left_top) + vec2(self.last_w, 0.0) },
});
self.right_bottom.y = 0.;
}
}
pub fn process_cursor(&mut self, last_cursor: usize, offset: usize, x: f32, y: f32, h: f32, z: f32) {
if offset == self.head {
if self.next_index > 0 && self.next_index - 1 == last_cursor {
self.last_cursor = Some(self.cursors.len());
}
self.emit_cursor(x, y, h, z);
}
}
pub fn process_geom(&mut self, x: f32, y: f32, w: f32, h: f32) {
if self.first {
self.first = false;
self.left_top.x = x;
self.left_top.y = y;
self.empty = true;
} else {
self.empty = false;
}
self.last_w = w;
self.right_bottom.x = x;
if y + h > self.right_bottom.y {
self.right_bottom.y = y + h;
}
}
pub fn process_newline(&mut self) {
if !self.first {
self.emit_selection_new_line();
self.first = true;
}
}
pub fn mark_text_select_only(&mut self, cursors: &[TextCursor], offset: usize, x: f32, y: f32, w: f32, h: f32) {
while offset >= self.end {
if offset == self.end {
self.process_geom(x, y, w, h);
self.emit_selection();
}
if !self.set_next(cursors) {
return;
}
}
if offset >= self.start && offset <= self.end {
self.process_geom(x, y, w, h);
if offset == self.end {
self.emit_selection();
}
}
}
pub fn mark_text_with_cursor(
&mut self,
cursors: &[TextCursor],
ch: char,
offset: usize,
x: f32,
y: f32,
w: f32,
h: f32,
z: f32,
last_cursor: usize,
mark_spaces: f32,
) -> f32 {
while offset >= self.end {
if offset == self.end {
self.process_cursor(last_cursor, offset, x, y, h, z);
self.process_geom(x, y, w, h);
self.emit_selection();
}
if !self.set_next(cursors) {
return mark_spaces;
}
}
if offset >= self.start && offset <= self.end {
self.process_cursor(last_cursor, offset, x, y, h, z);
self.process_geom(x, y, w, h);
if offset == self.end {
self.emit_selection();
}
if ch == '\n' {
return 0.0;
} else if ch == ' ' && offset < self.end {
return 2.0;
}
}
mark_spaces
}
}