ElyMark: the shell hears each load; the size limit follows the text (C14.4 r1)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
35923c124b
commit
e1ae303a1f
9 files changed
+106
-23
No files matched your search
@@ -424,7 +424,7 @@ fn large_banner(
|
||||
view: &Entity<MarkView>,
|
||||
cx: &mut Context<Desk>,
|
||||
) -> Option<ely_gpui_component::feedback::Banner> {
|
||||
if !view.read(cx).large {
|
||||
if !view.read(cx).large() {
|
||||
return None;
|
||||
}
|
||||
let size = view.read(cx).bytes() as f64 / (1024.0 * 1024.0);
|
||||
|
||||
@@ -108,7 +108,7 @@ impl Desk {
|
||||
}
|
||||
}
|
||||
|
||||
/// Line `line`, from zero, in `tab`, else in `file` once open.
|
||||
/// Line `line` in `tab`, or in `file` once open.
|
||||
pub fn open_at(
|
||||
&mut self,
|
||||
tab: Option<u64>,
|
||||
|
||||
+23
-13
@@ -1,4 +1,4 @@
|
||||
//! One Markdown file in a document, its text kept for the shell.
|
||||
//! One Markdown file, its text kept for the shell.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -12,7 +12,7 @@ use gpui::{
|
||||
|
||||
use crate::engine::{Doc, mark};
|
||||
|
||||
/// The text changed, or a new read of it is in.
|
||||
/// The text changed, or a read is in.
|
||||
pub struct Reported;
|
||||
|
||||
/// What went wrong, for the shell to say.
|
||||
@@ -58,7 +58,7 @@ pub struct Heading {
|
||||
pub line: usize,
|
||||
}
|
||||
|
||||
/// Where the reader was: the caret, and the first line in view.
|
||||
/// The caret and the first line in view.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Place {
|
||||
pub line: u64,
|
||||
@@ -78,8 +78,6 @@ pub struct MarkView {
|
||||
pub mode: Mode,
|
||||
/// The file's folder: pictures resolve here.
|
||||
pub(crate) folder: PathBuf,
|
||||
/// Too long to read: source alone, every byte saved (MARK-14).
|
||||
pub large: bool,
|
||||
pub focus: FocusHandle,
|
||||
}
|
||||
|
||||
@@ -95,9 +93,10 @@ impl MarkView {
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let text = doc.lock().expect("the text lock").current.clone();
|
||||
// Past what the document reads: source alone (PERF-07).
|
||||
let large = text.len() > LARGEST_READ;
|
||||
let mode = if large { Mode::Source } else { Mode::Visual };
|
||||
let mode = match text.len() > LARGEST_READ {
|
||||
true => Mode::Source,
|
||||
false => Mode::Visual,
|
||||
};
|
||||
let view = cx.weak_entity();
|
||||
let document = cx.new(|cx| {
|
||||
let mut document = MarkdownDocument::new(text, mode.shown(), window, cx);
|
||||
@@ -107,7 +106,9 @@ impl MarkView {
|
||||
});
|
||||
document
|
||||
});
|
||||
cx.subscribe(&document, Self::heard).detach();
|
||||
cx.subscribe_in(&document, window, Self::heard).detach();
|
||||
// The shell hears the load.
|
||||
cx.defer_in(window, |_, _, cx| cx.emit(Reported));
|
||||
let focus = cx.focus_handle();
|
||||
// The view's focus is the document's.
|
||||
cx.on_focus(&focus, window, |view, window, cx| {
|
||||
@@ -122,7 +123,6 @@ impl MarkView {
|
||||
saved,
|
||||
mode,
|
||||
folder,
|
||||
large,
|
||||
focus,
|
||||
}
|
||||
}
|
||||
@@ -141,6 +141,11 @@ impl MarkView {
|
||||
self.doc.lock().expect("the text lock").current.len()
|
||||
}
|
||||
|
||||
/// Too long to read: source alone (MARK-14).
|
||||
pub fn large(&self) -> bool {
|
||||
self.bytes() > LARGEST_READ
|
||||
}
|
||||
|
||||
pub fn modified(&self) -> bool {
|
||||
mark(&self.doc.lock().expect("the text lock").current) != self.saved
|
||||
}
|
||||
@@ -152,7 +157,7 @@ impl MarkView {
|
||||
|
||||
/// The same text in `mode` (MARK-02).
|
||||
pub fn switch(&mut self, mode: Mode, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.large && mode != Mode::Source {
|
||||
if self.large() && mode != Mode::Source {
|
||||
log::info!("mark: a large file stays in source");
|
||||
let said = ely_ui::i18n::text(cx, "mark.large.modes", &[]).to_string();
|
||||
return cx.emit(Failed(said));
|
||||
@@ -228,7 +233,7 @@ impl MarkView {
|
||||
self.document
|
||||
.update(cx, |document, cx| document.reset(text, cx));
|
||||
}
|
||||
let mode = if self.large { Mode::Source } else { mode };
|
||||
let mode = if self.large() { Mode::Source } else { mode };
|
||||
self.switch(mode, window, cx);
|
||||
if let Some(place) = place {
|
||||
self.document.update(cx, |document, cx| {
|
||||
@@ -250,14 +255,19 @@ impl MarkView {
|
||||
|
||||
fn heard(
|
||||
&mut self,
|
||||
document: Entity<MarkdownDocument>,
|
||||
document: &Entity<MarkdownDocument>,
|
||||
event: &DocumentEvent,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if *event == DocumentEvent::Changed {
|
||||
let text = document.read(cx).text(cx);
|
||||
self.doc.lock().expect("the text lock").current = text;
|
||||
self.changed += 1;
|
||||
if self.large() && self.mode != Mode::Source {
|
||||
log::info!("mark: the text grew past the read limit");
|
||||
self.switch(Mode::Source, window, cx);
|
||||
}
|
||||
}
|
||||
cx.emit(Reported);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use gpui::{Entity, TestAppContext, VisualTestContext};
|
||||
use gpui::{AppContext as _, Entity, TestAppContext, VisualTestContext};
|
||||
|
||||
use super::{MarkView, Mode};
|
||||
use super::{MarkView, Mode, Reported};
|
||||
use crate::engine::{Text, mark};
|
||||
use crate::text::Source;
|
||||
|
||||
@@ -48,14 +48,78 @@ fn typing_reaches_the_text_the_shell_saves(cx: &mut TestAppContext) {
|
||||
assert!(modified && changed > 0);
|
||||
}
|
||||
|
||||
fn current(view: &Entity<MarkView>, cx: &mut VisualTestContext) -> String {
|
||||
view.read_with(cx, |view, _| {
|
||||
view.doc.lock().expect("the text lock").current.clone()
|
||||
})
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn a_refresh_is_saved_text_and_keeps_no_undo(cx: &mut TestAppContext) {
|
||||
let (view, cx) = open("one\n", cx);
|
||||
let disk = Source::decode(b"two\n".to_vec()).expect("reads");
|
||||
view.update(cx, |view, cx| view.adopt("edited\n".into(), cx));
|
||||
cx.run_until_parked();
|
||||
assert!(view.read_with(cx, |view, _| view.modified()));
|
||||
let disk = Source::decode(b"from disk\n".to_vec()).expect("reads");
|
||||
view.update(cx, |view, cx| view.refresh(disk, cx));
|
||||
cx.run_until_parked();
|
||||
assert_eq!(current(&view, cx), "from disk\n");
|
||||
assert!(!view.read_with(cx, |view, _| view.modified()));
|
||||
assert_eq!(view.read_with(cx, |view, _| view.bytes()), 4);
|
||||
view.update_in(cx, |view, window, cx| view.switch(Mode::Source, window, cx));
|
||||
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");
|
||||
cx.run_until_parked();
|
||||
assert_eq!(current(&view, cx), "from disk\n", "undo stops at the disk");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn the_shell_hears_a_load_it_subscribed_to(cx: &mut TestAppContext) {
|
||||
cx.update(ely_gpui_component::init_for_tests);
|
||||
cx.update(|cx| {
|
||||
ely_ui::settings::install_defaults(cx);
|
||||
ely_ui::i18n::install(cx);
|
||||
});
|
||||
let heard = std::rc::Rc::new(std::cell::Cell::new(0));
|
||||
let (count, held) = (
|
||||
heard.clone(),
|
||||
std::rc::Rc::new(std::cell::RefCell::new(None)),
|
||||
);
|
||||
let holder = held.clone();
|
||||
let (_root, cx) = cx.add_window_view(move |window, cx| {
|
||||
let source = Source::decode(b"# Opened\n".to_vec()).expect("reads");
|
||||
let saved = mark(&source.text);
|
||||
let doc = Arc::new(Mutex::new(Text::new(source)));
|
||||
let folder = std::env::temp_dir();
|
||||
let view = cx.new(|cx| MarkView::new(doc, saved, folder, window, cx));
|
||||
// As the shell: subscribed after the view is made.
|
||||
let count = count.clone();
|
||||
cx.subscribe(&view, move |_, _, _: &Reported, _| {
|
||||
count.set(count.get() + 1)
|
||||
})
|
||||
.detach();
|
||||
holder.replace(Some(view));
|
||||
gpui::Empty
|
||||
});
|
||||
cx.run_until_parked();
|
||||
assert!(heard.get() > 0);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn a_text_grown_past_the_limit_goes_to_source(cx: &mut TestAppContext) {
|
||||
let (view, cx) = open("# Old\n", cx);
|
||||
let long = "x".repeat(ely_gpui_component::documents::LARGEST_READ + 1);
|
||||
view.update(cx, |view, cx| view.adopt(long, cx));
|
||||
cx.run_until_parked();
|
||||
assert!(view.read_with(cx, |view, _| view.large()));
|
||||
assert_eq!(view.read_with(cx, |view, _| view.mode), Mode::Source);
|
||||
view.update_in(cx, |view, window, cx| {
|
||||
view.switch(Mode::Preview, window, cx)
|
||||
});
|
||||
assert_eq!(view.read_with(cx, |view, _| view.mode), Mode::Source);
|
||||
let outline = view.read_with(cx, |view, cx| view.outline(cx));
|
||||
assert!(outline.is_empty(), "no stale heading: {outline:?}");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
||||
Reference in new issue
Block a user