use super::CodeEditor;
impl CodeEditor {
pub(crate) fn clear_selection(&mut self) {
self.cursors.clear_all_selections();
self.overlay_cache.clear();
}
pub(crate) fn get_selection_range(
&self,
) -> Option<((usize, usize), (usize, usize))> {
self.cursors.primary().selection_range()
}
pub(crate) fn get_selected_text(&self) -> Option<String> {
let (start, end) = self.get_selection_range()?;
if start == end {
return None; }
Some(self.extract_text_range(start, end))
}
pub(crate) fn extract_text_range(
&self,
start: (usize, usize),
end: (usize, usize),
) -> String {
let mut result = String::new();
if start.0 == end.0 {
let line = self.buffer.line(start.0);
if let Some((start_byte, _)) = line.char_indices().nth(start.1) {
let end_byte = line
.char_indices()
.nth(end.1)
.map_or(line.len(), |(idx, _)| idx);
result.push_str(&line[start_byte..end_byte]);
}
} else {
let first_line = self.buffer.line(start.0);
if let Some((start_byte, _)) =
first_line.char_indices().nth(start.1)
{
result.push_str(&first_line[start_byte..]);
result.push('\n');
}
for line_idx in (start.0 + 1)..end.0 {
result.push_str(self.buffer.line(line_idx));
result.push('\n');
}
let last_line = self.buffer.line(end.0);
let end_byte = last_line
.char_indices()
.nth(end.1)
.map_or(last_line.len(), |(idx, _)| idx);
result.push_str(&last_line[..end_byte]);
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_selection_single_line() {
let mut editor = CodeEditor::new("hello world", "py");
editor.cursors.primary_mut().anchor = Some((0, 0));
editor.cursors.primary_mut().position = (0, 5);
let text = editor.get_selected_text();
assert_eq!(text, Some("hello".to_string()));
}
#[test]
fn test_selection_multiline() {
let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
editor.cursors.primary_mut().anchor = Some((0, 2));
editor.cursors.primary_mut().position = (2, 3);
let text = editor.get_selected_text();
assert_eq!(text, Some("ne1\nline2\nlin".to_string()));
}
#[test]
fn test_selection_range_normalization() {
let mut editor = CodeEditor::new("hello world", "py");
editor.cursors.primary_mut().anchor = Some((0, 5));
editor.cursors.primary_mut().position = (0, 0);
let range = editor.get_selection_range();
assert_eq!(range, Some(((0, 0), (0, 5))));
}
#[test]
fn test_clear_selection() {
let mut editor = CodeEditor::new("hello world", "py");
editor.cursors.primary_mut().anchor = Some((0, 0));
editor.cursors.primary_mut().position = (0, 5);
editor.clear_selection();
assert!(!editor.cursors.primary().has_selection());
}
#[test]
fn test_selection_out_of_bounds() {
let mut editor = CodeEditor::new("hello", "py");
editor.cursors.primary_mut().anchor = Some((0, 10));
editor.cursors.primary_mut().position = (0, 15);
let text = editor.get_selected_text();
assert_eq!(text, Some("".to_string()));
}
#[test]
fn test_selection_multiline_out_of_bounds() {
let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
editor.cursors.primary_mut().anchor = Some((0, 10));
editor.cursors.primary_mut().position = (2, 3);
let text = editor.get_selected_text();
assert_eq!(text, Some("line2\nlin".to_string()));
editor.cursors.primary_mut().anchor = Some((0, 2));
editor.cursors.primary_mut().position = (2, 10);
let text = editor.get_selected_text();
assert_eq!(text, Some("ne1\nline2\nline3".to_string()));
}
#[test]
fn test_selection_unicode() {
let mut editor = CodeEditor::new("你好\n世界", "txt");
editor.cursors.primary_mut().anchor = Some((0, 1));
editor.cursors.primary_mut().position = (1, 1);
let text = editor.get_selected_text();
assert_eq!(text, Some("好\n世".to_string()));
}
#[test]
fn test_selection_with_empty_lines() {
let mut editor = CodeEditor::new("line1\n\nline3", "txt");
editor.cursors.primary_mut().anchor = Some((0, 0));
editor.cursors.primary_mut().position = (2, 5);
let text = editor.get_selected_text();
assert_eq!(text, Some("line1\n\nline3".to_string()));
}
#[test]
fn test_selection_emoji() {
let mut editor = CodeEditor::new("a😀b", "txt");
editor.cursors.primary_mut().anchor = Some((0, 1));
editor.cursors.primary_mut().position = (0, 2);
let text = editor.get_selected_text();
assert_eq!(text, Some("😀".to_string()));
}
#[test]
#[allow(clippy::unwrap_used)]
fn test_selection_complex_emoji() {
let complex_emoji = "👨👩👧👦";
let mut editor = CodeEditor::new(complex_emoji, "txt");
let char_count = complex_emoji.chars().count();
editor.cursors.primary_mut().anchor = Some((0, 0));
editor.cursors.primary_mut().position = (0, char_count);
let text = editor.get_selected_text();
assert_eq!(text, Some(complex_emoji.to_string()));
if char_count > 1 {
editor.cursors.primary_mut().anchor = Some((0, 0));
editor.cursors.primary_mut().position = (0, 1);
let text = editor.get_selected_text();
let first_char = complex_emoji.chars().next().unwrap().to_string();
assert_eq!(text, Some(first_char));
}
}
}