diff --git a/Cargo.lock b/Cargo.lock index 9f8a29e..d6b5f15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1922,7 +1922,7 @@ dependencies = [ [[package]] name = "ely-gpui-component" version = "0.1.1" -source = "git+https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components?rev=d126a7e7f7977dddae598486dd99aba79ce1f5f0#d126a7e7f7977dddae598486dd99aba79ce1f5f0" +source = "git+https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components?rev=e89fb32f43b51c0cd03f6be383dc58796bac19e1#e89fb32f43b51c0cd03f6be383dc58796bac19e1" dependencies = [ "alacritty_terminal", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 3185cdc..5de3ad6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ jiff = "0.2" anyhow = "1" directories = "6" dunce = "1" -ely-gpui-component = { git = "https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components", rev = "d126a7e7f7977dddae598486dd99aba79ce1f5f0" } +ely-gpui-component = { git = "https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components", rev = "e89fb32f43b51c0cd03f6be383dc58796bac19e1" } env_logger = "0.11" gpui = { package = "gpui-pre", version = "=0.3.8", default-features = false, features = ["stacker"] } gpui_platform = { package = "gpui-pre-platform", version = "=0.3.8", features = ["font-kit", "wayland", "x11"] } diff --git a/agents.md b/agents.md index 2a859f1..c496713 100644 --- a/agents.md +++ b/agents.md @@ -40,7 +40,7 @@ site/ 官网(Vite 8、pnpm、TypeScript,无框架;等轴 SVG 积木 ## 组件映射 -依赖 `ely-gpui-component`(pin d126a7e,gpui 为 crates.io 的 `gpui-pre` =0.3.8)。名字取自 `reference/Ely-GPUI-Components/src`。gpui 用法规则见该仓库 `AGENTS.md`。`tests/scripts/agents_md.py` 核对下表每个名字都是组件库公开项。 +依赖 `ely-gpui-component`(pin e89fb32,gpui 为 crates.io 的 `gpui-pre` =0.3.8)。名字取自 `reference/Ely-GPUI-Components/src`。gpui 用法规则见该仓库 `AGENTS.md`。`tests/scripts/agents_md.py` 核对下表每个名字都是组件库公开项。 | 需要 | 用 | | --- | --- | diff --git a/apps/ely-mark/src/compare.rs b/apps/ely-mark/src/compare.rs index 4b0097b..bb4aaee 100644 --- a/apps/ely-mark/src/compare.rs +++ b/apps/ely-mark/src/compare.rs @@ -190,3 +190,36 @@ enum Choice { Mine, Merged, } + +#[cfg(test)] +mod tests { + use gpui::TestAppContext; + + use super::Choice; + use crate::outside::tests::{look, opened, shown}; + + #[gpui::test] + fn a_merge_lands_as_one_undo_step(cx: &mut TestAppContext) { + let (desk, cx, id, path, _dirs) = opened("a\n\nb\n", cx); + let view = desk.read_with(cx, |desk, _| desk.active_view().expect("a tab")); + view.update(cx, |view, cx| view.adopt("a\n\nb\n\nmine\n".into(), cx)); + cx.run_until_parked(); + std::fs::write(&path, "theirs\n\na\n\nb\n").expect("an outside write"); + look(&desk, id, cx); + desk.update(cx, |desk, cx| desk.compare(id, cx)); + desk.update_in(cx, |desk, window, cx| { + desk.settle(Choice::Merged, window, cx) + }); + cx.run_until_parked(); + assert_eq!(shown(&desk, cx), "theirs\n\na\n\nb\n\nmine\n"); + let focus = view.read_with(cx, |view, _| view.focus.clone()); + cx.update(|window, cx| window.focus(&focus, cx)); + cx.run_until_parked(); + cx.simulate_keystrokes("secondary-z"); + assert_eq!( + shown(&desk, cx), + "a\n\nb\n\nmine\n", + "one undo takes the merge back" + ); + } +} diff --git a/apps/ely-mark/src/outside.rs b/apps/ely-mark/src/outside.rs index 025800d..c8c08a9 100644 --- a/apps/ely-mark/src/outside.rs +++ b/apps/ely-mark/src/outside.rs @@ -403,27 +403,4 @@ fn watch(dirs: &[PathBuf], cx: &mut Context) -> notify::Result ( + Entity, + &'static mut VisualTestContext, + u64, + PathBuf, + [tempfile::TempDir; 2], +) { + let (data, files) = ( + tempfile::tempdir().expect("data"), + tempfile::tempdir().expect("files"), + ); + let path = files.path().join("a.md"); + std::fs::write(&path, text).expect("a file"); + let (desk, cx) = start(cx, data.path()); + open(&desk, &path, cx); + let id = desk.read_with(cx, |desk, _| desk.core.active.expect("a tab")); + (desk, cx, id, path, [data, files]) +} + +/// The desk looks at the file, as the watcher would. +pub(crate) fn look(desk: &Entity, id: u64, cx: &mut VisualTestContext) { + desk.update(cx, |desk, cx| desk.look(id, cx)); + cx.run_until_parked(); + cx.executor().advance_clock(GAP * 2); + cx.run_until_parked(); +} + +pub(crate) fn shown(desk: &Entity, cx: &mut VisualTestContext) -> String { + desk.read_with(cx, |desk, cx| { + let view = desk.active_view().expect("a tab"); + view.read(cx).document().read(cx).text(cx) + }) +} + +#[gpui::test] +fn a_clean_tab_takes_the_disk_and_keeps_its_place(cx: &mut TestAppContext) { + let (desk, cx, id, path, _dirs) = opened("one\n\ntwo\n\nthree\n", cx); + let view = desk.read_with(cx, |desk, _| desk.active_view().expect("a tab")); + let document = view.read_with(cx, |view, _| view.document().clone()); + document.update(cx, |document, cx| document.set_caret(4, 2, cx)); + std::fs::write(&path, "uno\n\ndos\n\ntres, longer\n").expect("an outside write"); + look(&desk, id, cx); + assert_eq!(shown(&desk, cx), "uno\n\ndos\n\ntres, longer\n"); + assert!(!view.read_with(cx, |view, _| view.modified())); + assert_eq!( + document.read_with(cx, |document, cx| document.caret(cx)), + (4, 2) + ); + let focus = view.read_with(cx, |view, _| view.focus.clone()); + cx.update(|window, cx| window.focus(&focus, cx)); + cx.run_until_parked(); + cx.simulate_keystrokes("secondary-z"); + assert_eq!( + shown(&desk, cx), + "uno\n\ndos\n\ntres, longer\n", + "undo stops at the disk" + ); +} + +#[gpui::test] +fn a_deleted_file_keeps_its_text_and_says_so(cx: &mut TestAppContext) { + let (desk, cx, id, path, _dirs) = opened("kept\n", cx); + std::fs::remove_file(&path).expect("deleted"); + look(&desk, id, cx); + let deleted = desk.read_with(cx, |desk, _| { + matches!(desk.tab(id).expect("open").extra.outside, Outside::Deleted) + }); + assert!(deleted); + assert_eq!(shown(&desk, cx), "kept\n"); +} + +#[gpui::test] +fn a_renamed_file_is_followed(cx: &mut TestAppContext) { + let (desk, cx, id, path, _dirs) = opened("moved\n", cx); + let to = path.with_file_name("b.md"); + std::fs::rename(&path, &to).expect("renamed"); + look(&desk, id, cx); + let now = desk.read_with(cx, |desk, _| { + desk.tab(id) + .and_then(|tab| tab.session.path().map(PathBuf::from)) + }); + assert_eq!( + now.map(|path| path.file_name().map(ToOwned::to_owned)), + Some(Some("b.md".into())) + ); +} + +#[gpui::test] +fn a_path_that_cannot_be_read_says_why(cx: &mut TestAppContext) { + let (desk, cx, id, path, _dirs) = opened("text\n", cx); + std::fs::remove_file(&path).expect("removed"); + std::fs::create_dir(&path).expect("a folder in its place"); + look(&desk, id, cx); + let unread = desk.read_with(cx, |desk, _| { + matches!( + desk.tab(id).expect("open").extra.outside, + Outside::Unread(_) + ) + }); + assert!(unread); +} diff --git a/scripts/fetch-references.sh b/scripts/fetch-references.sh index 17f3f37..606ed04 100755 --- a/scripts/fetch-references.sh +++ b/scripts/fetch-references.sh @@ -36,7 +36,7 @@ fetch() { # name url rev [sparse paths...] echo "fetch $name $rev" } -fetch Ely-GPUI-Components https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components.git d126a7e7f7977dddae598486dd99aba79ce1f5f0 +fetch Ely-GPUI-Components https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components.git e89fb32f43b51c0cd03f6be383dc58796bac19e1 fetch zed https://github.com/zed-industries/zed.git 1a28cff4b409169bac058bca40dfbfeb7621d19b fetch libreoffice-core https://github.com/LibreOffice/core.git 8221e31b3ac356a1623c672912a3d2b492f7e3d1 \ include/LibreOfficeKit libreofficekit desktop/source/lib desktop/inc \