Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 87 additions & 39 deletions src/bin/pure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,67 @@ impl App {
}
}

fn indent_selection_or_cursor(&mut self) -> bool {
if let Some(selection) = self.current_selection() {
if self.display.indent_selection(&selection) {
self.selection_anchor = None;
self.mark_dirty();
self.display.set_preferred_column(None);
return true;
}
return false;
}

if self.display.indent_current_paragraph() {
self.selection_anchor = None;
self.mark_dirty();
self.display.set_preferred_column(None);
return true;
}

false
}

fn unindent_selection_or_cursor(&mut self) -> bool {
if let Some(selection) = self.current_selection() {
if self.display.unindent_selection(&selection) {
self.selection_anchor = None;
self.mark_dirty();
self.display.set_preferred_column(None);
return true;
}
return false;
}

if self.display.unindent_current_paragraph() {
self.selection_anchor = None;
self.mark_dirty();
self.display.set_preferred_column(None);
return true;
}

false
}

fn insert_char_with_selection(&mut self, ch: char) -> bool {
let mut selection_changed = false;
if let Some(selection) = self.current_selection() {
if !self.display.remove_selection(&selection) {
return false;
}
self.selection_anchor = None;
selection_changed = true;
}

let inserted = self.display.insert_char(ch);
if selection_changed || inserted {
self.mark_dirty();
self.display.set_preferred_column(None);
}

inserted
}

fn capture_reveal_toggle_snapshot(&self) -> RevealToggleSnapshot {
let viewport = self.display.last_view_height().max(1);
let max_scroll = self
Expand Down Expand Up @@ -1101,8 +1162,25 @@ impl App {
fn execute_menu_action(&mut self, action: MenuAction) -> bool {
match action {
MenuAction::SetParagraphType(kind) => {
if self.display.set_paragraph_type(kind) {
let handled = if let Some(selection) = self.current_selection() {
if self
.display
.set_paragraph_type_for_selection(&selection, kind)
{
self.mark_dirty();
self.selection_anchor = None;
true
} else {
false
}
} else if self.display.set_paragraph_type(kind) {
self.mark_dirty();
true
} else {
false
};

if handled {
self.display.set_preferred_column(None);
}
true
Expand All @@ -1118,19 +1196,11 @@ impl App {
true
}
MenuAction::IndentMore => {
if self.display.indent_current_paragraph() {
self.selection_anchor = None;
self.mark_dirty();
self.display.set_preferred_column(None);
}
self.indent_selection_or_cursor();
true
}
MenuAction::IndentLess => {
if self.display.unindent_current_paragraph() {
self.selection_anchor = None;
self.mark_dirty();
self.display.set_preferred_column(None);
}
self.unindent_selection_or_cursor();
true
}
}
Expand Down Expand Up @@ -1640,20 +1710,10 @@ impl App {
self.should_quit = true;
}
(KeyCode::Char(']'), m) if m.contains(KeyModifiers::CONTROL) => {
self.prepare_selection(false);
if self.display.indent_current_paragraph() {
self.selection_anchor = None;
self.mark_dirty();
self.display.set_preferred_column(None);
}
self.indent_selection_or_cursor();
}
(KeyCode::Char('['), m) if m.contains(KeyModifiers::CONTROL) => {
self.prepare_selection(false);
if self.display.unindent_current_paragraph() {
self.selection_anchor = None;
self.mark_dirty();
self.display.set_preferred_column(None);
}
self.unindent_selection_or_cursor();
}
(KeyCode::Left, m)
if m.contains(KeyModifiers::SHIFT | KeyModifiers::CONTROL) =>
Expand Down Expand Up @@ -1728,10 +1788,7 @@ impl App {
self.display.move_to_visual_line_start();
}
(KeyCode::Char('j'), m) if m.contains(KeyModifiers::CONTROL) => {
if self.display.insert_char('\n') {
self.mark_dirty();
self.display.set_preferred_column(None);
}
self.insert_char_with_selection('\n');
}
(KeyCode::Char('p'), m) if m.contains(KeyModifiers::CONTROL) => {
self.prepare_selection(false);
Expand Down Expand Up @@ -1780,28 +1837,19 @@ impl App {
}
(KeyCode::Enter, m) => {
if m.contains(KeyModifiers::SHIFT) || m.contains(KeyModifiers::CONTROL) {
if self.display.insert_char('\n') {
self.mark_dirty();
self.display.set_preferred_column(None);
}
self.insert_char_with_selection('\n');
} else if self.insert_paragraph_break() {
self.mark_dirty();
self.display.set_preferred_column(None);
}
}
(KeyCode::Tab, _) => {
if self.display.insert_char('\t') {
self.mark_dirty();
self.display.set_preferred_column(None);
}
self.insert_char_with_selection('\t');
}
(KeyCode::Char(ch), m)
if !m.contains(KeyModifiers::CONTROL) && !m.contains(KeyModifiers::ALT) =>
{
if self.display.insert_char(ch) {
self.mark_dirty();
self.display.set_preferred_column(None);
}
self.insert_char_with_selection(ch);
}
(KeyCode::Up, m) if m.contains(KeyModifiers::SHIFT) => {
self.prepare_selection(true);
Expand Down
184 changes: 184 additions & 0 deletions src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,190 @@ pub struct DocumentEditor {
}

impl DocumentEditor {
fn normalize_selection(
&self,
selection: &(CursorPointer, CursorPointer),
) -> Option<(CursorPointer, CursorPointer)> {
let mut start = selection.0.clone();
let mut end = selection.1.clone();

let ordering = self.compare_pointers(&start, &end)?;
match ordering {
Ordering::Less => {}
Ordering::Equal => return None,
Ordering::Greater => {
std::mem::swap(&mut start, &mut end);
}
}

Some((start, end))
}

pub(crate) fn selection_paragraph_targets(
&self,
selection: &(CursorPointer, CursorPointer),
) -> Option<Vec<CursorPointer>> {
if self.segments.is_empty() {
return None;
}

let (start, end) = self.normalize_selection(selection)?;
let start_key = self.pointer_key(&start)?;
let end_key = self.pointer_key(&end)?;

let mut targets: Vec<CursorPointer> = Vec::new();

for segment_index in start_key.segment_index..=end_key.segment_index {
let Some(segment) = self.segments.get(segment_index) else {
continue;
};
if segment.kind != SegmentKind::Text {
continue;
}

let len = segment.len;
let seg_start = if segment_index == start_key.segment_index {
start_key.offset.min(len)
} else {
0
};
let seg_end = if segment_index == end_key.segment_index {
end_key.offset.min(len)
} else {
len
};

let touched = if start_key.segment_index == end_key.segment_index {
seg_start < seg_end
} else if segment_index == start_key.segment_index {
seg_start < len || segment_index < end_key.segment_index
} else if segment_index == end_key.segment_index {
seg_end > 0
} else {
true
};

if !touched {
continue;
}

if targets
.iter()
.any(|pointer| pointer.paragraph_path == segment.paragraph_path)
{
continue;
}

targets.push(CursorPointer {
paragraph_path: segment.paragraph_path.clone(),
span_path: segment.span_path.clone(),
offset: if segment_index == start_key.segment_index {
seg_start
} else {
0
},
segment_kind: SegmentKind::Text,
});
}

if targets.is_empty() {
return None;
}

let mut keyed: Vec<(PointerKey, CursorPointer)> = Vec::new();
let mut unkeyed: Vec<CursorPointer> = Vec::new();
for pointer in targets {
if let Some(key) = self.pointer_key(&pointer) {
keyed.push((key, pointer));
} else {
unkeyed.push(pointer);
}
}

keyed.sort_by(|a, b| b.0.cmp(&a.0));

let mut ordered: Vec<CursorPointer> = keyed.into_iter().map(|(_, ptr)| ptr).collect();
ordered.extend(unkeyed);

Some(ordered)
}

pub(crate) fn pointer_for_character(&self, target: char) -> Option<CursorPointer> {
for segment in &self.segments {
if segment.kind != SegmentKind::Text {
continue;
}
let Some(text) = self.segment_text(segment) else {
continue;
};
for (idx, ch) in text.chars().enumerate() {
if ch == target {
return Some(CursorPointer {
paragraph_path: segment.paragraph_path.clone(),
span_path: segment.span_path.clone(),
offset: idx,
segment_kind: SegmentKind::Text,
});
}
}
}
None
}

pub(crate) fn remove_char_at_pointer(&mut self, pointer: &CursorPointer) -> bool {
if remove_char_at(&mut self.document, pointer, pointer.offset) {
self.update_segments_for_paragraph(&pointer.paragraph_path);
true
} else {
false
}
}

pub fn remove_selection(&mut self, selection: &(CursorPointer, CursorPointer)) -> bool {
if self.segments.is_empty() {
return false;
}

let (start, end) = match self.normalize_selection(selection) {
Some(bounds) => bounds,
None => return false,
};

let mut moved = self.move_to_pointer(&end);
if !moved {
moved =
self.fallback_move_to_text(&end, false) || self.fallback_move_to_text(&end, true);
}
if !moved {
return false;
}

loop {
match self.compare_pointers(&self.cursor, &start) {
Some(Ordering::Greater) => {
if !self.backspace() {
return false;
}
}
Some(Ordering::Equal) => break,
Some(Ordering::Less) => {
let mut moved_to_start = self.move_to_pointer(&start);
if !moved_to_start {
moved_to_start = self.fallback_move_to_text(&start, false)
|| self.fallback_move_to_text(&start, true);
}
if !moved_to_start {
return false;
}
break;
}
None => return false,
}
}

true
}

pub fn new(mut document: Document) -> Self {
ensure_document_initialized(&mut document);
let mut editor = Self {
Expand Down
Loading
Loading