use super::*; #[tokio::test(flavor = "multi_thread")] async fn test_split_write_quit_all() -> anyhow::Result<()> { let mut file1 = tempfile::NamedTempFile::new()?; let mut file2 = tempfile::NamedTempFile::new()?; let mut file3 = tempfile::NamedTempFile::new()?; let mut app = helpers::AppBuilder::new() .with_file(file1.path(), None) .build()?; test_key_sequences( &mut app, vec![ ( Some(&format!( "ihello1:sp:o {}ihello2:sp:o {}ihello3", file2.path().to_string_lossy(), file3.path().to_string_lossy() )), Some(&|app| { let docs: Vec<_> = app.editor.documents().collect(); assert_eq!(3, docs.len()); let doc1 = docs .iter() .find(|doc| doc.path().unwrap() == file1.path()) .unwrap(); assert_eq!("hello1", doc1.text().to_string()); let doc2 = docs .iter() .find(|doc| doc.path().unwrap() == file2.path()) .unwrap(); assert_eq!("hello2", doc2.text().to_string()); let doc3 = docs .iter() .find(|doc| doc.path().unwrap() == file3.path()) .unwrap(); assert_eq!("hello3", doc3.text().to_string()); helpers::assert_status_not_error(&app.editor); assert_eq!(3, app.editor.tree.views().count()); }), ), ( Some(":wqa"), Some(&|app| { helpers::assert_status_not_error(&app.editor); assert_eq!(0, app.editor.tree.views().count()); }), ), ], true, ) .await?; helpers::assert_file_has_content(file1.as_file_mut(), "hello1")?; helpers::assert_file_has_content(file2.as_file_mut(), "hello2")?; helpers::assert_file_has_content(file3.as_file_mut(), "hello3")?; Ok(()) } #[tokio::test(flavor = "multi_thread")] async fn test_split_write_quit_same_file() -> anyhow::Result<()> { let mut file = tempfile::NamedTempFile::new()?; let mut app = helpers::AppBuilder::new() .with_file(file.path(), None) .build()?; test_key_sequences( &mut app, vec![ ( Some("Oihello:spogoodbye"), Some(&|app| { assert_eq!(2, app.editor.tree.views().count()); helpers::assert_status_not_error(&app.editor); let mut docs: Vec<_> = app.editor.documents().collect(); assert_eq!(1, docs.len()); let doc = docs.pop().unwrap(); assert_eq!( helpers::platform_line("hello\ngoodbye"), doc.text().to_string() ); assert!(doc.is_modified()); }), ), ( Some(":wq"), Some(&|app| { helpers::assert_status_not_error(&app.editor); assert_eq!(1, app.editor.tree.views().count()); let mut docs: Vec<_> = app.editor.documents().collect(); assert_eq!(1, docs.len()); let doc = docs.pop().unwrap(); assert_eq!( helpers::platform_line("hello\ngoodbye"), doc.text().to_string() ); assert!(!doc.is_modified()); }), ), ], false, ) .await?; helpers::assert_file_has_content( file.as_file_mut(), &helpers::platform_line("hello\ngoodbye"), )?; Ok(()) } #[tokio::test(flavor = "multi_thread")] async fn test_changes_in_splits_apply_to_all_views() -> anyhow::Result<()> { // See . // Transactions must be applied to any view that has the changed document open. // This sequence would panic since the jumplist entry would be modified in one // window but not the other. Attempting to update the changelist in the other // window would cause a panic since it would point outside of the document. // The key sequence here: // * v Create a vertical split of the current buffer. // Both views look at the same doc. // * [ Add a line ending to the beginning of the document. // The cursor is now at line 2 in window 2. // * Save that selection to the jumplist in window 2. // * w Switch to window 1. // * kd Delete line 1 in window 1. // * q Close window 1, focusing window 2. // * d Delete line 1 in window 2. // // This panicked in the past because the jumplist entry on line 2 of window 2 // was not updated and after the `kd` step, pointed outside of the document. test(("#[|]#", "v[wkdqd", "#[|]#")).await?; // Transactions are applied to the views for windows lazily when they are focused. // This case panics if the transactions and inversions are not applied in the // correct order as we switch between windows. test(( "#[|]#", "[[[vuuuwUUUquuu", "#[|]#", )) .await?; // See . // This sequence undoes part of the history and then adds new changes, creating a // new branch in the history tree. `View::sync_changes` applies transactions down // and up to the lowest common ancestor in the path between old and new revision // numbers. If we apply these up/down transactions in the wrong order, this case // panics. // The key sequence: // * 3[ Create three empty lines so we are at the end of the document. // * v Create a split and save that point at the end of the document // in the jumplist. // * w Switch back to the first window. // * uu Undo twice (not three times which would bring us back to the // root of the tree). // * 3[ Create three empty lines. Now the end of the document is past // where it was on step 1. // * q Close window 1, focusing window 2 and causing a sync. This step // panics if we don't apply in the right order. // * %d Clean up the buffer. test(( "#[|]#", "3[vwuu3[q%d", "#[|]#", )) .await?; Ok(()) }