use std::collections::HashMap;
use super::{scroll_editor_content, DeleteSelectionWith};
use crate::{caret_locate::{locate_col, CaretColLocationInLine},
caret_scroll_index,
col,
empty_check_early_return,
multiline_disabled_check_early_return,
CaretScrAdj,
EditorArgsMut,
EditorBuffer,
EditorEngine,
GCString,
GCStringExt,
InlineString,
InlineVec,
RowIndex};
pub fn insert_chunk_at_caret(args: EditorArgsMut<'_>, chunk: &str) {
let EditorArgsMut { buffer, engine } = args;
let caret_scr_adj = buffer.get_caret_scr_adj();
let row_index_scr_adj = caret_scr_adj.row_index;
if buffer.line_at_row_index(row_index_scr_adj).is_some() {
insert_into_existing_line(EditorArgsMut { buffer, engine }, caret_scr_adj, chunk);
} else {
fill_in_missing_lines_up_to_row(
EditorArgsMut { buffer, engine },
row_index_scr_adj,
);
insert_chunk_into_new_line(
EditorArgsMut { buffer, engine },
caret_scr_adj,
chunk,
);
}
}
pub fn insert_new_line_at_caret(args: EditorArgsMut<'_>) {
let EditorArgsMut { buffer, engine } = args;
multiline_disabled_check_early_return!(engine, @Nothing);
if buffer.is_empty() {
{
let buffer_mut = buffer.get_mut(engine.viewport());
buffer_mut.inner.lines.push("".grapheme_string());
}
return;
}
match locate_col(buffer) {
CaretColLocationInLine::AtEnd => {
inner::insert_new_line_at_end_of_current_line(EditorArgsMut {
buffer,
engine,
});
}
CaretColLocationInLine::AtStart => {
inner::insert_new_line_at_start_of_current_line(EditorArgsMut {
buffer,
engine,
});
}
CaretColLocationInLine::InMiddle => {
inner::insert_new_line_at_middle_of_current_line(EditorArgsMut {
buffer,
engine,
});
}
}
mod inner {
use super::*;
pub fn insert_new_line_at_end_of_current_line(args: EditorArgsMut<'_>) {
let EditorArgsMut { buffer, engine } = args;
{
let buffer_mut = buffer.get_mut(engine.viewport());
let new_row_index = scroll_editor_content::inc_caret_row(
buffer_mut.inner.caret_raw,
buffer_mut.inner.scr_ofs,
buffer_mut.inner.vp.row_height,
);
scroll_editor_content::reset_caret_col(
buffer_mut.inner.caret_raw,
buffer_mut.inner.scr_ofs,
);
buffer_mut
.inner
.lines
.insert(new_row_index.as_usize(), "".grapheme_string());
}
}
pub fn insert_new_line_at_start_of_current_line(args: EditorArgsMut<'_>) {
let EditorArgsMut { buffer, engine } = args;
{
let buffer_mut = buffer.get_mut(engine.viewport());
let cur_row_index =
(*buffer_mut.inner.caret_raw + *buffer_mut.inner.scr_ofs).row_index;
buffer_mut
.inner
.lines
.insert(cur_row_index.as_usize(), "".grapheme_string());
}
{
let buffer_mut = buffer.get_mut(engine.viewport());
scroll_editor_content::inc_caret_row(
buffer_mut.inner.caret_raw,
buffer_mut.inner.scr_ofs,
buffer_mut.inner.vp.row_height,
);
}
}
pub fn insert_new_line_at_middle_of_current_line(args: EditorArgsMut<'_>) {
let EditorArgsMut { buffer, engine } = args;
if let Some(line) = buffer.line_at_caret_scr_adj() {
let caret_adj = buffer.get_caret_scr_adj();
let col_index = caret_adj.col_index;
let split_result = line.split_at_display_col(col_index);
if let Some((left_string, right_string)) = split_result {
let row_index = caret_adj.row_index.as_usize();
{
let buffer_mut = buffer.get_mut(engine.viewport());
let _ = std::mem::replace(
&mut buffer_mut.inner.lines[row_index],
left_string.grapheme_string(),
);
buffer_mut
.inner
.lines
.insert(row_index + 1, right_string.grapheme_string());
scroll_editor_content::inc_caret_row(
buffer_mut.inner.caret_raw,
buffer_mut.inner.scr_ofs,
buffer_mut.inner.vp.row_height,
);
scroll_editor_content::reset_caret_col(
buffer_mut.inner.caret_raw,
buffer_mut.inner.scr_ofs,
);
}
}
}
}
}
}
pub fn delete_at_caret(
buffer: &mut EditorBuffer,
engine: &mut EditorEngine,
) -> Option<()> {
empty_check_early_return!(buffer, @None);
if buffer.string_at_caret().is_some() {
inner::delete_in_middle_of_line(buffer, engine)?;
} else {
inner::delete_at_end_of_line(buffer, engine)?;
}
return None;
mod inner {
use super::*;
use crate::inline_string;
pub fn delete_in_middle_of_line(
buffer: &mut EditorBuffer,
engine: &mut EditorEngine,
) -> Option<()> {
let line = buffer.line_at_caret_scr_adj()?;
let new_line_content =
line.delete_char_at_col(buffer.get_caret_scr_adj().col_index)?;
{
let buffer_mut = buffer.get_mut(engine.viewport());
let row_index =
(*buffer_mut.inner.caret_raw + *buffer_mut.inner.scr_ofs).row_index;
let _ = std::mem::replace(
&mut buffer_mut.inner.lines[row_index.as_usize()],
new_line_content.grapheme_string(),
);
}
None
}
pub fn delete_at_end_of_line(
buffer: &mut EditorBuffer,
engine: &mut EditorEngine,
) -> Option<()> {
let this_line = buffer.line_at_caret_scr_adj()?;
let next_line = buffer.next_line_below_caret_to_string()?;
let new_line_content =
inline_string!("{a}{b}", a = this_line.string, b = next_line.string);
{
let buffer_mut = buffer.get_mut(engine.viewport());
let row_index =
(*buffer_mut.inner.caret_raw + *buffer_mut.inner.scr_ofs).row_index;
let _ = std::mem::replace(
&mut buffer_mut.inner.lines[row_index.as_usize()],
new_line_content.grapheme_string(),
);
buffer_mut.inner.lines.remove(row_index.as_usize() + 1);
}
None
}
}
}
pub fn backspace_at_caret(
buffer: &mut EditorBuffer,
engine: &mut EditorEngine,
) -> Option<()> {
empty_check_early_return!(buffer, @None);
match buffer.string_to_left_of_caret() {
Some(seg_result) => {
inner::backspace_in_middle_of_line(buffer, engine, seg_result.start_at)?;
}
None => {
inner::backspace_at_start_of_line(buffer, engine)?;
}
}
return None;
mod inner {
use super::*;
use crate::{inline_string, row, ColIndex};
pub fn backspace_in_middle_of_line(
buffer: &mut EditorBuffer,
engine: &mut EditorEngine,
delete_at_this_display_col: ColIndex,
) -> Option<()> {
let cur_line = buffer.line_at_caret_scr_adj()?;
let new_line_content =
cur_line.delete_char_at_col(delete_at_this_display_col)?;
{
let buffer_mut = buffer.get_mut(engine.viewport());
let cur_row_index =
(*buffer_mut.inner.caret_raw + *buffer_mut.inner.scr_ofs).row_index;
let _ = std::mem::replace(
&mut buffer_mut.inner.lines[cur_row_index.as_usize()],
new_line_content.grapheme_string(),
);
let new_line_content_display_width =
buffer_mut.inner.lines[cur_row_index.as_usize()].display_width;
scroll_editor_content::set_caret_col_to(
delete_at_this_display_col,
buffer_mut.inner.caret_raw,
buffer_mut.inner.scr_ofs,
buffer_mut.inner.vp.col_width,
new_line_content_display_width,
);
}
None
}
pub fn backspace_at_start_of_line(
buffer: &mut EditorBuffer,
engine: &mut EditorEngine,
) -> Option<()> {
let this_line = buffer.line_at_caret_scr_adj()?;
let prev_line = buffer.prev_line_above_caret()?;
let prev_line_display_width = {
let prev_row_index = buffer.get_caret_scr_adj().row_index - row(1);
buffer.get_line_display_width_at_row_index(prev_row_index)
};
let new_line_content =
inline_string!("{a}{b}", a = prev_line.string, b = this_line.string);
{
let buffer_mut = buffer.get_mut(engine.viewport());
let prev_row_index =
(*buffer_mut.inner.caret_raw + *buffer_mut.inner.scr_ofs).row_index
- row(1);
let cur_row_index =
(*buffer_mut.inner.caret_raw + *buffer_mut.inner.scr_ofs).row_index;
let _ = std::mem::replace(
&mut buffer_mut.inner.lines[prev_row_index.as_usize()],
new_line_content.grapheme_string(),
);
let new_line_content_display_width =
buffer_mut.inner.lines[prev_row_index.as_usize()].display_width;
buffer_mut.inner.lines.remove(cur_row_index.as_usize());
scroll_editor_content::dec_caret_row(
buffer_mut.inner.caret_raw,
buffer_mut.inner.scr_ofs,
);
scroll_editor_content::set_caret_col_to(
caret_scroll_index::col_index_for_width(prev_line_display_width),
buffer_mut.inner.caret_raw,
buffer_mut.inner.scr_ofs,
buffer_mut.inner.vp.col_width,
new_line_content_display_width,
);
}
None
}
}
}
pub fn delete_selected(
buffer: &mut EditorBuffer,
engine: &mut EditorEngine,
with: DeleteSelectionWith,
) -> Option<()> {
empty_check_early_return!(buffer, @None);
if buffer.get_selection_list().is_empty() {
return None;
}
let my_selection_map = buffer.get_selection_list().clone();
let lines = buffer.get_lines();
let selected_row_indices = my_selection_map.get_ordered_indices();
let mut vec_row_indices_to_remove = InlineVec::<RowIndex>::new();
let mut map_lines_to_replace = HashMap::new();
for selected_row_index in selected_row_indices {
if let Some(selection_range) = my_selection_map.get(selected_row_index) {
let line_width =
buffer.get_line_display_width_at_row_index(selected_row_index);
let (start_col_index, end_col_index) = selection_range.as_tuple();
{
if start_col_index == col(0)
&& end_col_index
== caret_scroll_index::col_index_for_width(line_width)
{
vec_row_indices_to_remove.push(selected_row_index);
continue;
}
}
if selection_range.start() == selection_range.end() {
continue;
}
let line_gcs = lines.get(selected_row_index.as_usize())?.clone();
let keep_before_selected_str = line_gcs.clip(
col(0),
selection_range.get_start_display_col_index_as_width(),
);
let keep_after_selected_str = line_gcs.clip(end_col_index, line_width);
let mut remaining_text = InlineString::with_capacity(
keep_before_selected_str.len() + keep_after_selected_str.len(),
);
remaining_text.push_str(keep_before_selected_str);
remaining_text.push_str(keep_after_selected_str);
map_lines_to_replace.insert(selected_row_index, remaining_text);
}
}
{
let buffer_mut = buffer.get_mut(engine.viewport());
for row_index in map_lines_to_replace.keys() {
let new_line_content = map_lines_to_replace[row_index].clone();
let _ = std::mem::replace(
&mut buffer_mut.inner.lines[row_index.as_usize()],
new_line_content.grapheme_string(),
);
}
vec_row_indices_to_remove.reverse();
for row_index in vec_row_indices_to_remove {
buffer_mut.inner.lines.remove(row_index.as_usize());
}
let maybe_new_caret =
my_selection_map.get_caret_at_start_of_range_scroll_adjusted(with);
if let Some(new_caret_scr_adj) = maybe_new_caret {
let caret_raw = new_caret_scr_adj + *buffer_mut.inner.scr_ofs;
*buffer_mut.inner.caret_raw = caret_raw;
}
}
buffer.clear_selection();
None
}
fn insert_into_existing_line(
args: EditorArgsMut<'_>,
caret_scr_adj: CaretScrAdj,
chunk: &str,
) -> Option<()> {
let EditorArgsMut { buffer, engine } = args;
let row_index = caret_scr_adj.row_index;
let line = buffer.line_at_row_index(row_index)?;
let (new_line_content, chunk_display_width) =
line.insert_chunk_at_col(caret_scr_adj.col_index, chunk);
{
let buffer_mut = buffer.get_mut(engine.viewport());
let _ = std::mem::replace(
&mut buffer_mut.inner.lines[row_index.as_usize()],
new_line_content.grapheme_string(),
);
let new_line_content_display_width =
buffer_mut.inner.lines[row_index.as_usize()].display_width;
scroll_editor_content::inc_caret_col_by(
buffer_mut.inner.caret_raw,
buffer_mut.inner.scr_ofs,
chunk_display_width,
new_line_content_display_width,
buffer_mut.inner.vp.col_width,
);
}
None
}
fn fill_in_missing_lines_up_to_row(args: EditorArgsMut<'_>, row_index: RowIndex) {
let EditorArgsMut { buffer, engine } = args;
let max_row_index = row_index.as_usize();
if buffer.get_lines().get(max_row_index).is_none() {
for row_index in 0..max_row_index + 1 {
if buffer.get_lines().get(row_index).is_none() {
{
let buffer_mut = buffer.get_mut(engine.viewport());
buffer_mut.inner.lines.push("".grapheme_string());
}
}
}
}
}
fn insert_chunk_into_new_line(
args: EditorArgsMut<'_>,
caret_scr_adj: CaretScrAdj,
chunk: &str,
) -> Option<()> {
let EditorArgsMut { buffer, engine } = args;
let row_index_scr_adj = caret_scr_adj.row_index.as_usize();
let _ = buffer.get_lines().get(row_index_scr_adj)?;
{
let buffer_mut = buffer.get_mut(engine.viewport());
let new_content = chunk.grapheme_string();
let _ = std::mem::replace(
&mut buffer_mut.inner.lines[row_index_scr_adj],
new_content,
);
let line_content = &buffer_mut.inner.lines[row_index_scr_adj];
let line_content_display_width = line_content.display_width;
let col_amt = GCString::width(chunk);
scroll_editor_content::inc_caret_col_by(
buffer_mut.inner.caret_raw,
buffer_mut.inner.scr_ofs,
col_amt,
line_content_display_width,
buffer_mut.inner.vp.col_width,
);
}
None
}