use {
reovim_driver_undo::{UndoKey, UndoProviderRegistry},
reovim_kernel::api::v1::{Edit, RegisterContent},
};
use super::{Operator, OperatorContext, OperatorError, Range, registers};
#[derive(Debug, Clone, Copy)]
pub struct ChangeOperator;
impl Operator for ChangeOperator {
fn id(&self) -> &'static str {
"change"
}
#[allow(clippy::option_if_let_else)]
#[cfg_attr(coverage_nightly, coverage(off))]
fn execute(&self, ctx: &mut OperatorContext<'_>, range: Range) -> Result<(), OperatorError> {
let buffer_arc = ctx
.kernel
.buffers
.get(ctx.buffer_id)
.ok_or(OperatorError::BufferNotFound(ctx.buffer_id))?;
let mut buffer = buffer_arc.write();
let cursor_before = ctx.cursor_position;
let start = range.start;
let end = range.end;
let mut deleted_text = String::new();
let lines = buffer.lines();
let line_count = lines.len();
let delete_pos;
if range.is_linewise {
let clamped_end = end.line.min(line_count.saturating_sub(1));
for line_idx in start.line..=clamped_end {
if let Some(line) = lines.get(line_idx) {
deleted_text.push_str(line);
deleted_text.push('\n');
}
}
let delete_start = reovim_kernel::api::v1::Position::new(start.line, 0);
let delete_end = if clamped_end + 1 < line_count {
{
let end_line_content = &lines[clamped_end];
reovim_kernel::api::v1::Position::new(
clamped_end,
end_line_content.chars().count(),
)
}
} else {
let last_line = &lines[clamped_end];
reovim_kernel::api::v1::Position::new(clamped_end, last_line.chars().count())
};
delete_pos = delete_start;
buffer.delete_range(delete_start, delete_end);
} else if start.line == end.line {
let line = &lines[start.line];
let start_col = start.column.min(line.len());
let end_col = end.column.min(line.len());
if start_col < end_col {
deleted_text.push_str(&line[start_col..end_col]);
}
delete_pos = start;
buffer.delete_range(start, end);
} else {
for (line_idx, line) in lines.iter().enumerate().take(end.line + 1).skip(start.line) {
if line_idx == start.line {
let start_col = start.column.min(line.len());
deleted_text.push_str(&line[start_col..]);
deleted_text.push('\n');
} else if line_idx == end.line {
let end_col = end.column.min(line.len());
deleted_text.push_str(&line[..end_col]);
} else {
deleted_text.push_str(line);
deleted_text.push('\n');
}
}
delete_pos = start;
buffer.delete_range(start, end);
}
let cursor_after = delete_pos;
ctx.cursor_after = Some(cursor_after);
drop(buffer);
if !deleted_text.is_empty()
&& let Some(undo_registry) = ctx.kernel.services.get::<UndoProviderRegistry>()
&& let Some(undo_provider) = undo_registry.get(&UndoKey::Buffer)
{
let edit = Edit::Delete {
position: delete_pos,
text: deleted_text.clone(),
};
undo_provider.record(ctx.buffer_id, vec![edit], cursor_before, cursor_after);
}
let content = if range.is_linewise {
RegisterContent::linewise(deleted_text)
} else {
RegisterContent::characterwise(deleted_text)
};
registers::store_and_sync(ctx.kernel, ctx.registers, ctx.register, &content);
registers::push_to_history(ctx.clipboard_history, &content);
Ok(())
}
fn is_linewise(&self) -> bool {
false }
fn is_text_modifying(&self) -> bool {
true
}
}