ElyMark: outside changes tested through the window; a reload keeps its place (C14.11)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
7529d39d5a
commit
ea5f47d74e
7 files changed
+178
-28
No files matched your search
Generated
+1
-1
@@ -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",
|
||||
|
||||
+1
-1
@@ -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"] }
|
||||
|
||||
@@ -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` 核对下表每个名字都是组件库公开项。
|
||||
|
||||
| 需要 | 用 |
|
||||
| --- | --- |
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -403,27 +403,4 @@ fn watch(dirs: &[PathBuf], cx: &mut Context<Desk>) -> notify::Result<Recommended
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use ely_files::Action;
|
||||
|
||||
use super::Extra;
|
||||
|
||||
#[test]
|
||||
fn a_restarted_autosave_saves_the_next_edit() {
|
||||
let mut extra = Extra::default();
|
||||
extra.policy.set_has_path();
|
||||
extra.policy.edit(Instant::now());
|
||||
// A write was in flight when the disk changed.
|
||||
extra.writing = Some(1);
|
||||
extra.restart_autosave();
|
||||
// As autosave settles a heard write, then edits.
|
||||
if let Some(revision) = extra.writing.take() {
|
||||
extra.policy.save_finished(revision, true);
|
||||
}
|
||||
let now = Instant::now();
|
||||
extra.policy.edit(now);
|
||||
assert!(matches!(extra.policy.tick(now + Duration::from_secs(5)), Action::SaveDue(_)));
|
||||
}
|
||||
}
|
||||
pub(crate) mod tests;
|
||||
@@ -0,0 +1,140 @@
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use ely_files::Action;
|
||||
use ely_office::prelude::*;
|
||||
use gpui::{Entity, TestAppContext, VisualTestContext};
|
||||
|
||||
use super::{Extra, GAP, Outside};
|
||||
use crate::desk::{
|
||||
Desk,
|
||||
tests::{open, start},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn a_restarted_autosave_saves_the_next_edit() {
|
||||
let mut extra = Extra::default();
|
||||
extra.policy.set_has_path();
|
||||
extra.policy.edit(Instant::now());
|
||||
// A write was in flight when the disk changed.
|
||||
extra.writing = Some(1);
|
||||
extra.restart_autosave();
|
||||
// As autosave settles a heard write, then edits.
|
||||
if let Some(revision) = extra.writing.take() {
|
||||
extra.policy.save_finished(revision, true);
|
||||
}
|
||||
let now = Instant::now();
|
||||
extra.policy.edit(now);
|
||||
assert!(matches!(
|
||||
extra.policy.tick(now + Duration::from_secs(5)),
|
||||
Action::SaveDue(_)
|
||||
));
|
||||
}
|
||||
|
||||
/// A saved file open: its desk, tab and path.
|
||||
pub(crate) fn opened(
|
||||
text: &str,
|
||||
cx: &mut TestAppContext,
|
||||
) -> (
|
||||
Entity<Desk>,
|
||||
&'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<Desk>, 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<Desk>, 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);
|
||||
}
|
||||
@@ -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 \
|
||||
|
||||
Reference in new issue
Block a user