use super::cursor::{CursorPos, CursorSet};
use super::edit::EditOperation;
use super::selection::Selection;
use crate::widget::syntax::{Language, SyntaxHighlighter};
impl TextArea {
pub fn get_syntax_language(&self) -> Language {
self.highlighter
.as_ref()
.map(|h| h.get_language())
.unwrap_or(Language::None)
}
pub fn set_language(&mut self, language: Language) {
if language == Language::None {
self.highlighter = None;
} else {
self.highlighter = Some(SyntaxHighlighter::new(language));
}
}
pub fn get_content(&self) -> String {
self.lines.join("\n")
}
pub fn set_content(&mut self, text: &str) {
self.lines = text.lines().map(String::from).collect();
if self.lines.is_empty() {
self.lines.push(String::new());
}
self.cursors = CursorSet::default();
self.scroll = (0, 0);
self.undo_stack.clear();
self.redo_stack.clear();
}
pub fn line_count(&self) -> usize {
self.lines.len()
}
pub fn cursor_position(&self) -> (usize, usize) {
let pos = self.cursors.primary().pos;
(pos.line, pos.col)
}
pub fn cursor_positions(&self) -> Vec<(usize, usize)> {
self.cursors
.iter()
.map(|c| (c.pos.line, c.pos.col))
.collect()
}
pub fn cursor_count(&self) -> usize {
self.cursors.len()
}
pub fn set_cursor(&mut self, line: usize, col: usize) {
let line = line.min(self.lines.len().saturating_sub(1));
let col = col.min(self.line_len(line));
self.cursors = CursorSet::new(CursorPos::new(line, col));
}
pub(super) fn line_len(&self, line: usize) -> usize {
self.lines.get(line).map(|l| l.chars().count()).unwrap_or(0)
}
pub fn get_selection(&self) -> Option<String> {
let sel = self.cursors.primary().selection()?.normalized();
self.get_text_in_selection(&sel)
}
fn get_text_in_selection(&self, sel: &Selection) -> Option<String> {
let mut result = String::new();
for line_idx in sel.start.0..=sel.end.0 {
if line_idx >= self.lines.len() {
break;
}
let line = &self.lines[line_idx];
let char_count = line.chars().count();
let start_col = if line_idx == sel.start.0 {
sel.start.1
} else {
0
};
let end_col = if line_idx == sel.end.0 {
sel.end.1
} else {
char_count
};
if start_col < char_count {
let start_byte = crate::utils::text::char_to_byte_index(line, start_col);
let end_byte =
crate::utils::text::char_to_byte_index(line, end_col.min(char_count));
result.push_str(&line[start_byte..end_byte]);
}
if line_idx < sel.end.0 {
result.push('\n');
}
}
Some(result)
}
pub fn delete_selection(&mut self) {
let sel = match self.cursors.primary().selection() {
Some(s) => s.normalized(),
None => return,
};
if sel.start.0 == sel.end.0 {
if let Some(line) = self.lines.get_mut(sel.start.0) {
let char_count = line.chars().count();
let start_byte = crate::utils::text::char_to_byte_index(line, sel.start.1);
let end_byte =
crate::utils::text::char_to_byte_index(line, sel.end.1.min(char_count));
let deleted: String = line.drain(start_byte..end_byte).collect();
self.push_undo(EditOperation::Delete {
line: sel.start.0,
col: sel.start.1,
text: deleted,
});
}
} else {
let before: String = self
.lines
.get(sel.start.0)
.map(|l| l.chars().take(sel.start.1).collect())
.unwrap_or_default();
let after: String = self
.lines
.get(sel.end.0)
.map(|l| l.chars().skip(sel.end.1).collect())
.unwrap_or_default();
for _ in sel.start.0..=sel.end.0 {
if sel.start.0 < self.lines.len() {
self.lines.remove(sel.start.0);
}
}
self.lines
.insert(sel.start.0, format!("{}{}", before, after));
}
self.cursors = CursorSet::new(CursorPos::new(sel.start.0, sel.start.1));
}
pub fn has_selection(&self) -> bool {
self.cursors.primary().is_selecting()
}
pub fn start_selection(&mut self) {
self.cursors.primary_mut().start_selection();
}
pub(super) fn update_selection(&mut self) {
}
pub fn clear_selection(&mut self) {
for cursor in self.cursors.iter_mut() {
cursor.clear_selection();
}
}
pub(super) fn push_undo(&mut self, op: EditOperation) {
self.undo_stack.push(op);
if self.undo_stack.len() > super::MAX_UNDO_HISTORY {
self.undo_stack.remove(0);
}
self.redo_stack.clear();
}
pub(super) fn set_primary_cursor(&mut self, line: usize, col: usize) {
let line = line.min(self.lines.len().saturating_sub(1));
let col = col.min(self.line_len(line));
self.cursors.set_primary(CursorPos::new(line, col));
}
}
use super::TextArea;