So much better
This commit is contained in:
@@ -60,6 +60,40 @@ pub struct Editor {
|
||||
search: SearchState,
|
||||
}
|
||||
|
||||
impl Editor {
|
||||
pub fn start_selection(&mut self) {
|
||||
self.text.start_selection();
|
||||
}
|
||||
|
||||
pub fn cancel_selection(&mut self) {
|
||||
self.text.cancel_selection();
|
||||
}
|
||||
|
||||
pub fn is_selecting(&self) -> bool {
|
||||
self.text.is_selecting()
|
||||
}
|
||||
|
||||
pub fn copy(&mut self) {
|
||||
self.text.copy();
|
||||
}
|
||||
|
||||
pub fn cut(&mut self) -> bool {
|
||||
self.text.cut()
|
||||
}
|
||||
|
||||
pub fn paste(&mut self) -> bool {
|
||||
self.text.paste()
|
||||
}
|
||||
|
||||
pub fn select_all(&mut self) {
|
||||
self.text.select_all();
|
||||
}
|
||||
|
||||
pub fn selection_range(&self) -> Option<((usize, usize), (usize, usize))> {
|
||||
self.text.selection_range()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Editor {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
@@ -300,6 +334,9 @@ impl Editor {
|
||||
pub fn render(&self, frame: &mut Frame, area: Rect, highlighter: Highlighter) {
|
||||
let (cursor_row, cursor_col) = self.text.cursor();
|
||||
let cursor_style = Style::default().bg(Color::White).fg(Color::Black);
|
||||
let selection_style = Style::default().bg(Color::Rgb(60, 80, 120));
|
||||
|
||||
let selection = self.text.selection_range();
|
||||
|
||||
let lines: Vec<Line> = self
|
||||
.text
|
||||
@@ -309,39 +346,30 @@ impl Editor {
|
||||
.map(|(row, line)| {
|
||||
let tokens = highlighter(row, line);
|
||||
let mut spans: Vec<Span> = Vec::new();
|
||||
let mut col = 0;
|
||||
|
||||
if row == cursor_row {
|
||||
let mut col = 0;
|
||||
for (style, text) in tokens {
|
||||
let text_len = text.chars().count();
|
||||
if cursor_col >= col && cursor_col < col + text_len {
|
||||
let before = text.chars().take(cursor_col - col).collect::<String>();
|
||||
let cursor_char =
|
||||
text.chars().nth(cursor_col - col).unwrap_or(' ');
|
||||
let after =
|
||||
text.chars().skip(cursor_col - col + 1).collect::<String>();
|
||||
for (base_style, text) in tokens {
|
||||
for ch in text.chars() {
|
||||
let is_cursor = row == cursor_row && col == cursor_col;
|
||||
let is_selected = is_in_selection(row, col, selection);
|
||||
|
||||
if !before.is_empty() {
|
||||
spans.push(Span::styled(before, style));
|
||||
}
|
||||
spans.push(Span::styled(cursor_char.to_string(), cursor_style));
|
||||
if !after.is_empty() {
|
||||
spans.push(Span::styled(after, style));
|
||||
}
|
||||
let style = if is_cursor {
|
||||
cursor_style
|
||||
} else if is_selected {
|
||||
base_style.bg(selection_style.bg.unwrap())
|
||||
} else {
|
||||
spans.push(Span::styled(text, style));
|
||||
}
|
||||
col += text_len;
|
||||
}
|
||||
if cursor_col >= col {
|
||||
spans.push(Span::styled(" ", cursor_style));
|
||||
}
|
||||
} else {
|
||||
for (style, text) in tokens {
|
||||
spans.push(Span::styled(text, style));
|
||||
base_style
|
||||
};
|
||||
|
||||
spans.push(Span::styled(ch.to_string(), style));
|
||||
col += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if row == cursor_row && cursor_col >= col {
|
||||
spans.push(Span::styled(" ", cursor_style));
|
||||
}
|
||||
|
||||
Line::from(spans)
|
||||
})
|
||||
.collect();
|
||||
@@ -468,3 +496,23 @@ impl Editor {
|
||||
fn is_word_char(c: char) -> bool {
|
||||
c.is_alphanumeric() || matches!(c, '!' | '@' | '?' | '.' | ':' | '_' | '#')
|
||||
}
|
||||
|
||||
fn is_in_selection(row: usize, col: usize, selection: Option<((usize, usize), (usize, usize))>) -> bool {
|
||||
let Some(((start_row, start_col), (end_row, end_col))) = selection else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if row < start_row || row > end_row {
|
||||
return false;
|
||||
}
|
||||
|
||||
if row == start_row && row == end_row {
|
||||
col >= start_col && col < end_col
|
||||
} else if row == start_row {
|
||||
col >= start_col
|
||||
} else if row == end_row {
|
||||
col < end_col
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user