From 7d5b4088a65692f0a2e2d83bd0b163fff07f5616 Mon Sep 17 00:00:00 2001 From: vasilito Date: Sat, 20 Jun 2026 13:01:14 +0300 Subject: [PATCH] =?UTF-8?q?tlc:=20fix=20F5/F6/F8=20to=20MC=20parity=20?= =?UTF-8?q?=E2=80=94=20no-op=20on=20no=20selection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts F5/F6/F8 from operating on current line (TLC extension) to MC behavior: do nothing when no selection is active. MC's eval_marks returns false when mark1==mark2, so F5/F6/F8 in MC are no-ops without a selection. Removes the now-dead copy_line/cut_line/delete_line_no_sel helpers and their tests. Replaces with MC-parity no-op tests. Extracts current_line_range() helper from select_current_line and duplicate_line_or_selection (also covers unindent_selection pattern). Removes ~50 lines of duplicated line-range computation. --- .../tui/tlc/source/src/editor/handlers.rs | 38 +++--- .../recipes/tui/tlc/source/src/editor/mod.rs | 119 ++++-------------- 2 files changed, 37 insertions(+), 120 deletions(-) diff --git a/local/recipes/tui/tlc/source/src/editor/handlers.rs b/local/recipes/tui/tlc/source/src/editor/handlers.rs index 13d075de63..0d374268e6 100644 --- a/local/recipes/tui/tlc/source/src/editor/handlers.rs +++ b/local/recipes/tui/tlc/source/src/editor/handlers.rs @@ -396,44 +396,34 @@ impl Editor { } return EditorResult::Running; } - // F5 — Copy block (or current line if no selection). + // F5 — Copy block. MC parity: no-op when no selection. if key == Key::f(5) { - if self.cursor.has_selection() { - if let Some(text) = self.cursor.selected_text(&self.buffer) { - self.clipboard = Some(text.to_string()); - let _ = crate::editor::clipboard_osc52::osc52_copy(&text); - self.message = Some("Block copied".to_string()); - } - } else { - let _ = self.copy_line(); + if let Some(text) = self.cursor.selected_text(&self.buffer) { + self.clipboard = Some(text.to_string()); + let _ = crate::editor::clipboard_osc52::osc52_copy(&text); + self.message = Some("Block copied".to_string()); } return EditorResult::Running; } - // F6 — Move (cut) block (or current line if no selection). + // F6 — Move (cut) block. MC parity: no-op when no selection. if key == Key::f(6) { - if self.cursor.has_selection() { - if let Some(text) = self.cursor.selected_text(&self.buffer) { - self.clipboard = Some(text.to_string()); - let _ = crate::editor::clipboard_osc52::osc52_copy(&text); - self.cursor.delete_selection(&mut self.buffer); - self.cursor.set_position(self.buffer.cursor(), &self.buffer); - self.modified = true; - self.message = Some("Block moved".to_string()); - } - } else { - let _ = self.cut_line(); + if let Some(text) = self.cursor.selected_text(&self.buffer) { + self.clipboard = Some(text.to_string()); + let _ = crate::editor::clipboard_osc52::osc52_copy(&text); + self.cursor.delete_selection(&mut self.buffer); + self.cursor.set_position(self.buffer.cursor(), &self.buffer); + self.modified = true; + self.message = Some("Block moved".to_string()); } return EditorResult::Running; } - // F8 — Delete block (or current line if no selection). + // F8 — Delete block. MC parity: no-op when no selection. if key == Key::f(8) { if self.cursor.has_selection() { self.cursor.delete_selection(&mut self.buffer); self.cursor.set_position(self.buffer.cursor(), &self.buffer); self.modified = true; self.message = Some("Block deleted".to_string()); - } else { - let _ = self.delete_line_no_sel(); } return EditorResult::Running; } diff --git a/local/recipes/tui/tlc/source/src/editor/mod.rs b/local/recipes/tui/tlc/source/src/editor/mod.rs index a2eb30313f..2c31719267 100644 --- a/local/recipes/tui/tlc/source/src/editor/mod.rs +++ b/local/recipes/tui/tlc/source/src/editor/mod.rs @@ -737,105 +737,38 @@ bracket_flash: None, /// any). Cursor moves to the start of the next line; anchor is /// left at the start of the current line. pub fn select_current_line(&mut self) { - let total = self.buffer.as_string().len(); - let bytes = self.buffer.as_string().into_bytes(); - let mut line_start = self.buffer.cursor(); - while line_start > 0 && bytes[line_start - 1] != b'\n' { - line_start -= 1; - } - let mut line_end = self.buffer.cursor(); - while line_end < total && bytes[line_end] != b'\n' { - line_end += 1; - } - if line_end < total { - line_end += 1; - } + let (line_start, line_end) = self.current_line_range(); self.buffer.set_cursor(line_start); self.cursor.set_position(line_start, &self.buffer); self.cursor.start_selection(); self.cursor.set_position(line_end, &self.buffer); } - /// Copy the current line (with newline) to the clipboard when no - /// selection is active. Returns true if a line was copied. - pub fn copy_line(&mut self) -> bool { - if self.cursor.has_selection() { - return false; - } + /// Compute the byte range of the current line, starting at the + /// beginning of the line and ending just past the trailing + /// newline (or at end of buffer if the last line has no newline). + fn current_line_range(&self) -> (usize, usize) { let total = self.buffer.as_string().len(); let bytes = self.buffer.as_string().into_bytes(); - let mut line_start = self.buffer.cursor(); + let cursor = self.buffer.cursor(); + let mut line_start = cursor; while line_start > 0 && bytes[line_start - 1] != b'\n' { line_start -= 1; } - let mut line_end = self.buffer.cursor(); + let mut line_end = cursor; while line_end < total && bytes[line_end] != b'\n' { line_end += 1; } if line_end < total { line_end += 1; } - let text = String::from_utf8_lossy(&bytes[line_start..line_end]).into_owned(); - self.clipboard = Some(text.clone()); - let _ = crate::editor::clipboard_osc52::osc52_copy(&text); - self.message = Some("Line copied".to_string()); - true - } - - /// Cut the current line (with newline) to the clipboard when no - /// selection is active. Returns true if a line was cut. - pub fn cut_line(&mut self) -> bool { - if self.cursor.has_selection() { - return false; - } - let total = self.buffer.as_string().len(); - let bytes = self.buffer.as_string().into_bytes(); - let mut line_start = self.buffer.cursor(); - while line_start > 0 && bytes[line_start - 1] != b'\n' { - line_start -= 1; - } - let mut line_end = self.buffer.cursor(); - while line_end < total && bytes[line_end] != b'\n' { - line_end += 1; - } - if line_end < total { - line_end += 1; - } - let text = String::from_utf8_lossy(&bytes[line_start..line_end]).into_owned(); - self.clipboard = Some(text.clone()); - let _ = crate::editor::clipboard_osc52::osc52_copy(&text); - self.buffer.begin_undo_group(); - self.buffer.set_cursor(line_start); - for _ in line_start..line_end { - self.buffer.delete_forward(); - } - self.cursor.set_position(self.buffer.cursor(), &self.buffer); - self.cursor.clear_selection(); - self.buffer.end_undo_group(); - self.modified = true; - self.message = Some("Line cut".to_string()); - true - } - - /// Delete the current line (with newline) when no selection is - /// active. Returns true if a line was deleted. - pub fn delete_line_no_sel(&mut self) -> bool { - if self.cursor.has_selection() { - return false; - } - self.buffer.delete_line(); - self.cursor.set_position(self.buffer.cursor(), &self.buffer); - self.cursor.clear_selection(); - self.modified = true; - self.message = Some("Line deleted".to_string()); - true + (line_start, line_end) } /// Duplicate the current line (with newline) when no selection /// is active, or duplicate the selected block otherwise. /// Returns true if anything was duplicated. pub fn duplicate_line_or_selection(&mut self) -> bool { - let total = self.buffer.as_string().len(); let bytes = self.buffer.as_string().into_bytes(); if let Some((s, e)) = self.cursor.selection() { let text = String::from_utf8_lossy(&bytes[s..e]).into_owned(); @@ -849,17 +782,7 @@ bracket_flash: None, self.message = Some("Duplicated".to_string()); return true; } - let mut line_start = self.buffer.cursor(); - while line_start > 0 && bytes[line_start - 1] != b'\n' { - line_start -= 1; - } - let mut line_end = self.buffer.cursor(); - while line_end < total && bytes[line_end] != b'\n' { - line_end += 1; - } - if line_end < total { - line_end += 1; - } + let (line_start, line_end) = self.current_line_range(); let text = String::from_utf8_lossy(&bytes[line_start..line_end]).into_owned(); self.buffer.begin_undo_group(); self.buffer.set_cursor(line_end); @@ -2362,37 +2285,41 @@ mod tests { } #[test] - fn f5_without_selection_copies_line() { + fn f5_without_selection_is_noop_mc_parity() { let mut e = make_empty(); e.insert_str("first\nsecond\nthird\n"); e.buffer.set_cursor(8); e.cursor.set_position(8, &e.buffer); e.handle_key(Key::f(5)); - assert_eq!(e.clipboard.as_deref(), Some("second\n")); + assert_eq!(e.clipboard, None); assert_eq!(e.buffer().as_string(), "first\nsecond\nthird\n"); } #[test] - fn f6_without_selection_cuts_line() { + fn f6_without_selection_is_noop_mc_parity() { let mut e = make_empty(); e.insert_str("first\nsecond\nthird\n"); + e.buffer.mark_saved(); + e.modified = false; e.buffer.set_cursor(8); e.cursor.set_position(8, &e.buffer); e.handle_key(Key::f(6)); - assert_eq!(e.clipboard.as_deref(), Some("second\n")); - assert_eq!(e.buffer().as_string(), "first\nthird\n"); - assert!(e.is_modified()); + assert_eq!(e.clipboard, None); + assert_eq!(e.buffer().as_string(), "first\nsecond\nthird\n"); + assert!(!e.is_modified()); } #[test] - fn f8_without_selection_deletes_line() { + fn f8_without_selection_is_noop_mc_parity() { let mut e = make_empty(); e.insert_str("first\nsecond\nthird\n"); + e.buffer.mark_saved(); + e.modified = false; e.buffer.set_cursor(8); e.cursor.set_position(8, &e.buffer); e.handle_key(Key::f(8)); - assert_eq!(e.buffer().as_string(), "first\nthird\n"); - assert!(e.is_modified()); + assert_eq!(e.buffer().as_string(), "first\nsecond\nthird\n"); + assert!(!e.is_modified()); } #[test]