documents: undo in an untouched block steps the document; visual after preview opens an empty end
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
6f0401e967
commit
362a0fc9fa
4 files changed
+86
-8
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use gpui::{AppContext as _, Context, Focusable as _, Window};
|
use gpui::{App, AppContext as _, Context, Focusable as _, Window};
|
||||||
|
|
||||||
use super::{DocumentEvent, MarkdownDocument, Open};
|
use super::{DocumentEvent, MarkdownDocument, Open};
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -31,6 +31,24 @@ impl MarkdownDocument {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The open block still holds what it opened with.
|
||||||
|
pub(super) fn untouched(&self, cx: &App) -> bool {
|
||||||
|
self.open.as_ref().is_some_and(|open| {
|
||||||
|
open.input.read(cx).text() == &self.editor.read(cx).text()[open.range.clone()]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Undo or redo in the document's one history.
|
||||||
|
pub(super) fn history(&mut self, redo: bool, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.commit(cx);
|
||||||
|
self.editor.update(cx, |editor, cx| match redo {
|
||||||
|
true => editor.redo(cx),
|
||||||
|
false => editor.undo(cx),
|
||||||
|
});
|
||||||
|
window.focus(&self.focus, cx);
|
||||||
|
self.focused(window, cx);
|
||||||
|
}
|
||||||
|
|
||||||
/// Opens block `ix` as shown, or one past the end.
|
/// Opens block `ix` as shown, or one past the end.
|
||||||
pub(super) fn open(
|
pub(super) fn open(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -170,6 +170,8 @@ impl MarkdownDocument {
|
|||||||
}
|
}
|
||||||
let focus = self.focus_handle(cx);
|
let focus = self.focus_handle(cx);
|
||||||
window.focus(&focus, cx);
|
window.focus(&focus, cx);
|
||||||
|
// Preview to visual keeps one handle: no focus event.
|
||||||
|
self.focused(window, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -425,3 +425,50 @@ fn focus_opens_no_block_in_a_written_document(cx: &mut TestAppContext) {
|
|||||||
cx.simulate_input("x");
|
cx.simulate_input("x");
|
||||||
assert_eq!(text(&document, cx), TEXT);
|
assert_eq!(text(&document, cx), TEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mode(document: &Entity<MarkdownDocument>, mode: MarkdownMode, cx: &mut VisualTestContext) {
|
||||||
|
document.update_in(cx, |document, window, cx| {
|
||||||
|
document.set_mode(mode, window, cx)
|
||||||
|
});
|
||||||
|
cx.run_until_parked();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn preview_and_back_still_take_typing_in_an_empty_document(cx: &mut TestAppContext) {
|
||||||
|
let (document, cx) = open("", MarkdownMode::Visual, cx);
|
||||||
|
focus(&document, cx);
|
||||||
|
cx.simulate_keystrokes("escape");
|
||||||
|
mode(&document, MarkdownMode::Preview, cx);
|
||||||
|
mode(&document, MarkdownMode::Visual, cx);
|
||||||
|
cx.simulate_input("Hi");
|
||||||
|
assert_eq!(text(&document, cx), "Hi");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn undo_in_an_untouched_block_steps_the_document(cx: &mut TestAppContext) {
|
||||||
|
let (document, cx) = open("Seed", MarkdownMode::Source, cx);
|
||||||
|
document.update(cx, |document, cx| document.set_text("", cx));
|
||||||
|
mode(&document, MarkdownMode::Visual, cx);
|
||||||
|
cx.simulate_keystrokes("secondary-z");
|
||||||
|
cx.run_until_parked();
|
||||||
|
assert_eq!(text(&document, cx), "Seed", "the source edit is undone");
|
||||||
|
cx.simulate_keystrokes("secondary-shift-z");
|
||||||
|
cx.run_until_parked();
|
||||||
|
assert_eq!(text(&document, cx), "", "and redone");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn undo_in_a_typed_block_stays_in_the_block(cx: &mut TestAppContext) {
|
||||||
|
let (document, cx) = open("Seed", MarkdownMode::Source, cx);
|
||||||
|
document.update(cx, |document, cx| document.set_text(TEXT, cx));
|
||||||
|
mode(&document, MarkdownMode::Visual, cx);
|
||||||
|
press(1, Modifiers::none(), cx);
|
||||||
|
cx.simulate_input(" x");
|
||||||
|
cx.simulate_keystrokes("secondary-z");
|
||||||
|
cx.run_until_parked();
|
||||||
|
assert_eq!(
|
||||||
|
text(&document, cx),
|
||||||
|
TEXT,
|
||||||
|
"the typing went, not the edit before"
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ use gpui::{
|
|||||||
use super::MarkdownDocument;
|
use super::MarkdownDocument;
|
||||||
use crate::{
|
use crate::{
|
||||||
documents::{MarkdownMode, render::draw_nodes},
|
documents::{MarkdownMode, render::draw_nodes},
|
||||||
forms::{Down, Up},
|
forms::{self, Down, Up},
|
||||||
primitives::FocusRing as _,
|
primitives::FocusRing as _,
|
||||||
theme::{ActiveTheme, Radius, TextSize},
|
theme::{ActiveTheme, Radius, TextSize},
|
||||||
};
|
};
|
||||||
@@ -158,13 +158,24 @@ impl Render for MarkdownDocument {
|
|||||||
MarkdownMode::Visual => root
|
MarkdownMode::Visual => root
|
||||||
.key_context(CONTEXT)
|
.key_context(CONTEXT)
|
||||||
.track_focus(&self.focus)
|
.track_focus(&self.focus)
|
||||||
.on_action(cx.listener(|document, _: &Undo, _, cx| {
|
.on_action(cx.listener(|document, _: &Undo, window, cx| {
|
||||||
document.commit(cx);
|
document.history(false, window, cx);
|
||||||
document.editor.update(cx, |editor, cx| editor.undo(cx));
|
|
||||||
}))
|
}))
|
||||||
.on_action(cx.listener(|document, _: &Redo, _, cx| {
|
.on_action(cx.listener(|document, _: &Redo, window, cx| {
|
||||||
document.commit(cx);
|
document.history(true, window, cx);
|
||||||
document.editor.update(cx, |editor, cx| editor.redo(cx));
|
}))
|
||||||
|
// An untouched block leaves undo to the document.
|
||||||
|
.capture_action(cx.listener(|document, _: &forms::Undo, window, cx| {
|
||||||
|
if document.untouched(cx) {
|
||||||
|
cx.stop_propagation();
|
||||||
|
document.history(false, window, cx);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.capture_action(cx.listener(|document, _: &forms::Redo, window, cx| {
|
||||||
|
if document.untouched(cx) {
|
||||||
|
cx.stop_propagation();
|
||||||
|
document.history(true, window, cx);
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
.capture_action(cx.listener(|document, _: &Up, window, cx| {
|
.capture_action(cx.listener(|document, _: &Up, window, cx| {
|
||||||
if document.step(false, window, cx) {
|
if document.step(false, window, cx) {
|
||||||
|
|||||||
Reference in new issue
Block a user