#![cfg(feature = "vi")] use std::sync::OnceLock; use cosmic_text::{Buffer, Cursor, Edit, Metrics, SyntaxEditor, SyntaxSystem, ViEditor}; static SYNTAX_SYSTEM: OnceLock = OnceLock::new(); // New editor for tests fn editor() -> ViEditor<'static, 'static> { // More or less copied from cosmic-edit let font_size: f32 = 14.0; let line_height = (font_size * 1.4).ceil(); let metrics = Metrics::new(font_size, line_height); let buffer = Buffer::new_empty(metrics); let editor = SyntaxEditor::new( buffer, SYNTAX_SYSTEM.get_or_init(SyntaxSystem::new), "base16-eighties.dark", ) .expect("Default theme `base16-eighties.dark` should be found"); ViEditor::new(editor) } // Tests that inserting into an empty editor correctly sets the editor as modified. #[test] fn insert_in_empty_editor_sets_changed() { let mut editor = editor(); assert!(!editor.changed()); editor.start_change(); editor.insert_at(Cursor::new(0, 0), "Robert'); DROP TABLE Students;--", None); editor.finish_change(); assert!(editor.changed()); } // Tests an edge case where a save point is never set. // Undoing changes should set the editor back to unmodified. #[test] fn insert_and_undo_in_unsaved_editor_is_unchanged() { let mut editor = editor(); assert!(!editor.changed()); editor.start_change(); editor.insert_at(Cursor::new(0, 0), "loop {}", None); editor.finish_change(); assert!(editor.changed()); // Undoing the above change should set the editor as unchanged even if the save state is unset editor.start_change(); editor.undo(); editor.finish_change(); assert!(!editor.changed()); } #[test] fn undo_to_save_point_sets_editor_to_unchanged() { let mut editor = editor(); // Latest saved change is the first change editor.start_change(); let cursor = editor.insert_at(Cursor::new(0, 0), "Ferris is Rust's ", None); editor.finish_change(); assert!( editor.changed(), "Editor should be set to changed after insertion" ); editor.save_point(); assert!( !editor.changed(), "Editor should be set to unchanged after setting a save point" ); // A new insert should set the editor as modified and the pivot should still be on the first // change from earlier editor.start_change(); editor.insert_at(cursor, "mascot", None); editor.finish_change(); assert!( editor.changed(), "Editor should be set to changed after inserting text after a save point" ); // Undoing the latest change should set the editor to unmodified again editor.start_change(); editor.undo(); editor.finish_change(); assert!( !editor.changed(), "Editor should be set to unchanged after undoing to save point" ); } #[test] fn redoing_to_save_point_sets_editor_as_unchanged() { let mut editor = editor(); // Initial change assert!( !editor.changed(), "Editor should start in an unchanged state" ); editor.start_change(); editor.insert_at(Cursor::new(0, 0), "editor.start_change();", None); editor.finish_change(); assert!( editor.changed(), "Editor should be set as modified after insert() and finish_change()" ); editor.save_point(); assert!( !editor.changed(), "Editor should be unchanged after setting a save point" ); // Change to undo then redo editor.start_change(); editor.insert_at(Cursor::new(1, 0), "editor.finish_change()", None); editor.finish_change(); assert!( editor.changed(), "Editor should be set as modified after insert() and finish_change()" ); editor.save_point(); assert!( !editor.changed(), "Editor should be unchanged after setting a save point" ); editor.undo(); assert!( editor.changed(), "Undoing past save point should set editor as changed" ); editor.redo(); assert!( !editor.changed(), "Redoing to save point should set editor as unchanged" ); } #[test] fn redoing_past_save_point_sets_editor_to_changed() { let mut editor = editor(); // Save point change to undo to and then redo past. editor.start_change(); editor.insert_string("Walt Whitman ", None); editor.finish_change(); // Set save point to the change above. assert!( editor.changed(), "Editor should be set as modified after insert() and finish_change()" ); editor.save_point(); assert!( !editor.changed(), "Editor should be unchanged after setting a save point" ); editor.start_change(); editor.insert_string("Allen Ginsberg ", None); editor.finish_change(); editor.start_change(); editor.insert_string("Jack Kerouac ", None); editor.finish_change(); assert!(editor.changed(), "Editor should be modified insertion"); // Undo to Whitman editor.undo(); editor.undo(); assert!( !editor.changed(), "Editor should be unmodified after undoing to the save point" ); // Redo to Kerouac editor.redo(); editor.redo(); assert!( editor.changed(), "Editor should be modified after redoing past the save point" ); } #[test] fn undoing_past_save_point_sets_editor_to_changed() { let mut editor = editor(); editor.start_change(); editor.insert_string("Robert Fripp ", None); editor.finish_change(); // Save point change to undo past. editor.start_change(); editor.insert_string("Thurston Moore ", None); editor.finish_change(); assert!(editor.changed(), "Editor should be changed after insertion"); editor.save_point(); assert!( !editor.changed(), "Editor should be unchanged after setting a save point" ); editor.start_change(); editor.insert_string("Kim Deal ", None); editor.finish_change(); // Undo to the first change editor.undo(); editor.undo(); assert!( editor.changed(), "Editor should be changed after undoing past save point" ); } // #[test] // fn undo_all_changes() { // unimplemented!() // }