mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 06:52:21 -04:00
move remaining undo ops into separate files
This commit is contained in:
parent
ad74e26c84
commit
d70e35e0a2
7 changed files with 123 additions and 101 deletions
|
@ -1,13 +1,16 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
mod counts;
|
||||||
|
mod schema11;
|
||||||
|
mod tree;
|
||||||
|
mod undo;
|
||||||
|
|
||||||
pub use crate::backend_proto::{
|
pub use crate::backend_proto::{
|
||||||
deck_kind::Kind as DeckKind, filtered_search_term::FilteredSearchOrder, Deck as DeckProto,
|
deck_kind::Kind as DeckKind, filtered_search_term::FilteredSearchOrder, Deck as DeckProto,
|
||||||
DeckCommon, DeckKind as DeckKindProto, FilteredDeck, FilteredSearchTerm, NormalDeck,
|
DeckCommon, DeckKind as DeckKindProto, FilteredDeck, FilteredSearchTerm, NormalDeck,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{backend_proto as pb, markdown::render_markdown, text::sanitize_html_no_images};
|
||||||
backend_proto as pb, markdown::render_markdown, text::sanitize_html_no_images, undo::Undo,
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
collection::Collection,
|
collection::Collection,
|
||||||
deckconf::DeckConfID,
|
deckconf::DeckConfID,
|
||||||
|
@ -18,9 +21,6 @@ use crate::{
|
||||||
timestamp::TimestampSecs,
|
timestamp::TimestampSecs,
|
||||||
types::Usn,
|
types::Usn,
|
||||||
};
|
};
|
||||||
mod counts;
|
|
||||||
mod schema11;
|
|
||||||
mod tree;
|
|
||||||
pub(crate) use counts::DueCounts;
|
pub(crate) use counts::DueCounts;
|
||||||
pub use schema11::DeckSchema11;
|
pub use schema11::DeckSchema11;
|
||||||
use std::{borrow::Cow, sync::Arc};
|
use std::{borrow::Cow, sync::Arc};
|
||||||
|
@ -285,7 +285,7 @@ impl Collection {
|
||||||
// rename children
|
// rename children
|
||||||
col.rename_child_decks(&existing_deck, &deck.name, usn)?;
|
col.rename_child_decks(&existing_deck, &deck.name, usn)?;
|
||||||
}
|
}
|
||||||
col.update_single_deck_inner_undo_only(deck, &existing_deck)?;
|
col.update_single_deck_undoable(deck, &existing_deck)?;
|
||||||
if name_changed {
|
if name_changed {
|
||||||
// after updating, we need to ensure all grandparents exist, which may not be the case
|
// after updating, we need to ensure all grandparents exist, which may not be the case
|
||||||
// in the parent->child case
|
// in the parent->child case
|
||||||
|
@ -312,18 +312,6 @@ impl Collection {
|
||||||
self.storage.add_or_update_deck_with_existing_id(deck)
|
self.storage.add_or_update_deck_with_existing_id(deck)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update an individual, existing deck. Caller is responsible for ensuring deck
|
|
||||||
/// is normalized, matches parents, is not a duplicate name, and bumping mtime.
|
|
||||||
pub(crate) fn update_single_deck_inner_undo_only(
|
|
||||||
&mut self,
|
|
||||||
deck: &mut Deck,
|
|
||||||
original: &Deck,
|
|
||||||
) -> Result<()> {
|
|
||||||
self.state.deck_cache.clear();
|
|
||||||
self.save_undo(Box::new(DeckUpdated(original.clone())));
|
|
||||||
self.storage.update_deck(deck)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn ensure_deck_name_unique(&self, deck: &mut Deck, usn: Usn) -> Result<()> {
|
pub(crate) fn ensure_deck_name_unique(&self, deck: &mut Deck, usn: Usn) -> Result<()> {
|
||||||
loop {
|
loop {
|
||||||
match self.storage.get_deck_id(&deck.name)? {
|
match self.storage.get_deck_id(&deck.name)? {
|
||||||
|
@ -371,7 +359,7 @@ impl Collection {
|
||||||
let new_name = format!("{}\x1f{}", new_name, child_only.join("\x1f"));
|
let new_name = format!("{}\x1f{}", new_name, child_only.join("\x1f"));
|
||||||
child.name = new_name;
|
child.name = new_name;
|
||||||
child.set_modified(usn);
|
child.set_modified(usn);
|
||||||
self.update_single_deck_inner_undo_only(&mut child, &original)?;
|
self.update_single_deck_undoable(&mut child, &original)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -601,7 +589,7 @@ impl Collection {
|
||||||
deck.reset_stats_if_day_changed(today);
|
deck.reset_stats_if_day_changed(today);
|
||||||
mutator(&mut deck.common);
|
mutator(&mut deck.common);
|
||||||
deck.set_modified(usn);
|
deck.set_modified(usn);
|
||||||
self.update_single_deck_inner_undo_only(deck, &original)
|
self.update_single_deck_undoable(deck, &original)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drag_drop_decks(
|
pub fn drag_drop_decks(
|
||||||
|
@ -647,19 +635,6 @@ impl Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct DeckUpdated(Deck);
|
|
||||||
|
|
||||||
impl Undo for DeckUpdated {
|
|
||||||
fn undo(mut self: Box<Self>, col: &mut crate::collection::Collection) -> Result<()> {
|
|
||||||
let current = col
|
|
||||||
.storage
|
|
||||||
.get_deck(self.0.id)?
|
|
||||||
.ok_or_else(|| AnkiError::invalid_input("deck disappeared"))?;
|
|
||||||
col.update_single_deck_inner_undo_only(&mut self.0, ¤t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{human_deck_name_to_native, immediate_parent_name, normalize_native_name};
|
use super::{human_deck_name_to_native, immediate_parent_name, normalize_native_name};
|
||||||
|
|
33
rslib/src/decks/undo.rs
Normal file
33
rslib/src/decks/undo.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::undo::Undo;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct DeckUpdated(Deck);
|
||||||
|
|
||||||
|
impl Undo for DeckUpdated {
|
||||||
|
fn undo(mut self: Box<Self>, col: &mut crate::collection::Collection) -> Result<()> {
|
||||||
|
let current = col
|
||||||
|
.storage
|
||||||
|
.get_deck(self.0.id)?
|
||||||
|
.ok_or_else(|| AnkiError::invalid_input("deck disappeared"))?;
|
||||||
|
col.update_single_deck_undoable(&mut self.0, ¤t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collection {
|
||||||
|
/// Update an individual, existing deck. Caller is responsible for ensuring deck
|
||||||
|
/// is normalized, matches parents, is not a duplicate name, and bumping mtime.
|
||||||
|
/// Clears deck cache.
|
||||||
|
pub(super) fn update_single_deck_undoable(
|
||||||
|
&mut self,
|
||||||
|
deck: &mut Deck,
|
||||||
|
original: &Deck,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.state.deck_cache.clear();
|
||||||
|
self.save_undo(Box::new(DeckUpdated(original.clone())));
|
||||||
|
self.storage.update_deck(deck)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,10 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
mod undo;
|
||||||
|
|
||||||
|
use crate::serde::{default_on_invalid, deserialize_int_from_number};
|
||||||
use crate::{define_newtype, prelude::*};
|
use crate::{define_newtype, prelude::*};
|
||||||
use crate::{
|
|
||||||
serde::{default_on_invalid, deserialize_int_from_number},
|
|
||||||
undo::Undo,
|
|
||||||
};
|
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
@ -82,14 +81,6 @@ impl RevlogEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Collection {
|
impl Collection {
|
||||||
/// Add the provided revlog entry, modifying the ID if it is not unique.
|
|
||||||
pub(crate) fn add_revlog_entry(&mut self, mut entry: RevlogEntry) -> Result<RevlogID> {
|
|
||||||
entry.id = self.storage.add_revlog_entry(&entry, true)?;
|
|
||||||
let id = entry.id;
|
|
||||||
self.save_undo(Box::new(RevlogAdded(entry)));
|
|
||||||
Ok(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn log_manually_scheduled_review(
|
pub(crate) fn log_manually_scheduled_review(
|
||||||
&mut self,
|
&mut self,
|
||||||
card: &Card,
|
card: &Card,
|
||||||
|
@ -107,28 +98,7 @@ impl Collection {
|
||||||
taken_millis: 0,
|
taken_millis: 0,
|
||||||
review_kind: RevlogReviewKind::Manual,
|
review_kind: RevlogReviewKind::Manual,
|
||||||
};
|
};
|
||||||
self.add_revlog_entry(entry)?;
|
self.add_revlog_entry_undoable(entry)?;
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct RevlogAdded(RevlogEntry);
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct RevlogRemoved(RevlogEntry);
|
|
||||||
|
|
||||||
impl Undo for RevlogAdded {
|
|
||||||
fn undo(self: Box<Self>, col: &mut crate::collection::Collection) -> Result<()> {
|
|
||||||
col.storage.remove_revlog_entry(self.0.id)?;
|
|
||||||
col.save_undo(Box::new(RevlogRemoved(self.0)));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Undo for RevlogRemoved {
|
|
||||||
fn undo(self: Box<Self>, col: &mut crate::collection::Collection) -> Result<()> {
|
|
||||||
col.storage.add_revlog_entry(&self.0, false)?;
|
|
||||||
col.save_undo(Box::new(RevlogAdded(self.0)));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
36
rslib/src/revlog/undo.rs
Normal file
36
rslib/src/revlog/undo.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
use super::RevlogEntry;
|
||||||
|
use crate::{prelude::*, undo::Undo};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct RevlogAdded(RevlogEntry);
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct RevlogRemoved(RevlogEntry);
|
||||||
|
|
||||||
|
impl Undo for RevlogAdded {
|
||||||
|
fn undo(self: Box<Self>, col: &mut Collection) -> Result<()> {
|
||||||
|
col.storage.remove_revlog_entry(self.0.id)?;
|
||||||
|
col.save_undo(Box::new(RevlogRemoved(self.0)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Undo for RevlogRemoved {
|
||||||
|
fn undo(self: Box<Self>, col: &mut Collection) -> Result<()> {
|
||||||
|
col.storage.add_revlog_entry(&self.0, false)?;
|
||||||
|
col.save_undo(Box::new(RevlogAdded(self.0)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collection {
|
||||||
|
/// Add the provided revlog entry, modifying the ID if it is not unique.
|
||||||
|
pub(crate) fn add_revlog_entry_undoable(&mut self, mut entry: RevlogEntry) -> Result<RevlogID> {
|
||||||
|
entry.id = self.storage.add_revlog_entry(&entry, true)?;
|
||||||
|
let id = entry.id;
|
||||||
|
self.save_undo(Box::new(RevlogAdded(entry)));
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
}
|
|
@ -305,7 +305,7 @@ impl Collection {
|
||||||
answer.answered_at,
|
answer.answered_at,
|
||||||
answer.milliseconds_taken,
|
answer.milliseconds_taken,
|
||||||
);
|
);
|
||||||
self.add_revlog_entry(revlog)?;
|
self.add_revlog_entry_undoable(revlog)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
mod undo;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend_proto::TagTreeNode,
|
backend_proto::TagTreeNode,
|
||||||
collection::Collection,
|
collection::Collection,
|
||||||
|
@ -9,7 +11,6 @@ use crate::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
text::{normalize_to_nfc, to_re},
|
text::{normalize_to_nfc, to_re},
|
||||||
types::Usn,
|
types::Usn,
|
||||||
undo::Undo,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use regex::{NoExpand, Regex, Replacer};
|
use regex::{NoExpand, Regex, Replacer};
|
||||||
|
@ -244,23 +245,11 @@ impl Collection {
|
||||||
} else if let Cow::Owned(new_name) = normalized_name {
|
} else if let Cow::Owned(new_name) = normalized_name {
|
||||||
tag.name = new_name;
|
tag.name = new_name;
|
||||||
}
|
}
|
||||||
self.register_tag_inner(&tag)?;
|
self.register_tag_undoable(&tag)?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds an already-validated tag to the DB and undo list.
|
|
||||||
/// Caller is responsible for setting usn.
|
|
||||||
pub(crate) fn register_tag_inner(&mut self, tag: &Tag) -> Result<()> {
|
|
||||||
self.save_undo(Box::new(AddedTag(tag.clone())));
|
|
||||||
self.storage.register_tag(&tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn remove_single_tag(&mut self, tag: &Tag) -> Result<()> {
|
|
||||||
self.save_undo(Box::new(RemovedTag(tag.clone())));
|
|
||||||
self.storage.remove_single_tag(&tag.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If parent tag(s) exist and differ in case, return a rewritten tag.
|
/// If parent tag(s) exist and differ in case, return a rewritten tag.
|
||||||
fn adjusted_case_for_parents(&self, tag: &str) -> Result<Option<String>> {
|
fn adjusted_case_for_parents(&self, tag: &str) -> Result<Option<String>> {
|
||||||
if let Some(parent_tag) = self.first_existing_parent_tag(&tag)? {
|
if let Some(parent_tag) = self.first_existing_parent_tag(&tag)? {
|
||||||
|
@ -482,24 +471,6 @@ impl Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct AddedTag(Tag);
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct RemovedTag(Tag);
|
|
||||||
|
|
||||||
impl Undo for AddedTag {
|
|
||||||
fn undo(self: Box<Self>, col: &mut Collection) -> Result<()> {
|
|
||||||
col.remove_single_tag(&self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Undo for RemovedTag {
|
|
||||||
fn undo(self: Box<Self>, col: &mut Collection) -> Result<()> {
|
|
||||||
col.register_tag_inner(&self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
37
rslib/src/tags/undo.rs
Normal file
37
rslib/src/tags/undo.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
use super::Tag;
|
||||||
|
use crate::{prelude::*, undo::Undo};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct AddedTag(Tag);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct RemovedTag(Tag);
|
||||||
|
|
||||||
|
impl Undo for AddedTag {
|
||||||
|
fn undo(self: Box<Self>, col: &mut Collection) -> Result<()> {
|
||||||
|
col.remove_single_tag_undoable(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Undo for RemovedTag {
|
||||||
|
fn undo(self: Box<Self>, col: &mut Collection) -> Result<()> {
|
||||||
|
col.register_tag_undoable(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collection {
|
||||||
|
/// Adds an already-validated tag to the DB and undo list.
|
||||||
|
/// Caller is responsible for setting usn.
|
||||||
|
pub(super) fn register_tag_undoable(&mut self, tag: &Tag) -> Result<()> {
|
||||||
|
self.save_undo(Box::new(AddedTag(tag.clone())));
|
||||||
|
self.storage.register_tag(&tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_single_tag_undoable(&mut self, tag: &Tag) -> Result<()> {
|
||||||
|
self.save_undo(Box::new(RemovedTag(tag.clone())));
|
||||||
|
self.storage.remove_single_tag(&tag.name)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue