avoid bumping mtime when nothing has changed
+ update sync indicator after every op + skip mtime bump on undo/redo
This commit is contained in:
parent
6cc713cbe8
commit
aa7d2721c9
@ -738,6 +738,9 @@ class AnkiQt(QMainWindow):
|
|||||||
if not focused and dirty:
|
if not focused and dirty:
|
||||||
self.fade_out_webview()
|
self.fade_out_webview()
|
||||||
|
|
||||||
|
if changes.mtime:
|
||||||
|
self.toolbar.update_sync_status()
|
||||||
|
|
||||||
def on_focus_did_change(
|
def on_focus_did_change(
|
||||||
self, new_focus: Optional[QWidget], _old: Optional[QWidget]
|
self, new_focus: Optional[QWidget], _old: Optional[QWidget]
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -1571,6 +1571,7 @@ message OpChanges {
|
|||||||
bool notetype = 5;
|
bool notetype = 5;
|
||||||
bool config = 6;
|
bool config = 6;
|
||||||
bool deck_config = 11;
|
bool deck_config = 11;
|
||||||
|
bool mtime = 12;
|
||||||
|
|
||||||
bool browser_table = 7;
|
bool browser_table = 7;
|
||||||
bool browser_sidebar = 8;
|
bool browser_sidebar = 8;
|
||||||
|
@ -18,6 +18,7 @@ impl From<OpChanges> for pb::OpChanges {
|
|||||||
notetype: c.changes.notetype,
|
notetype: c.changes.notetype,
|
||||||
config: c.changes.config,
|
config: c.changes.config,
|
||||||
deck_config: c.changes.deck_config,
|
deck_config: c.changes.deck_config,
|
||||||
|
mtime: c.changes.mtime,
|
||||||
browser_table: c.requires_browser_table_redraw(),
|
browser_table: c.requires_browser_table_redraw(),
|
||||||
browser_sidebar: c.requires_browser_sidebar_redraw(),
|
browser_sidebar: c.requires_browser_sidebar_redraw(),
|
||||||
editor: c.requires_editor_redraw(),
|
editor: c.requires_editor_redraw(),
|
||||||
|
@ -13,18 +13,22 @@ impl Collection {
|
|||||||
self.storage.begin_rust_trx()?;
|
self.storage.begin_rust_trx()?;
|
||||||
self.begin_undoable_operation(op);
|
self.begin_undoable_operation(op);
|
||||||
|
|
||||||
let mut res = func(self);
|
func(self)
|
||||||
|
// any changes mean an mtime bump
|
||||||
if res.is_ok() {
|
.and_then(|out| {
|
||||||
if let Err(e) = self.set_modified() {
|
if !have_op || (self.current_undo_step_has_changes() && !self.undoing_or_redoing())
|
||||||
res = Err(e);
|
{
|
||||||
} else if let Err(e) = self.storage.commit_rust_trx() {
|
self.set_modified()?;
|
||||||
res = Err(e);
|
|
||||||
}
|
}
|
||||||
}
|
Ok(out)
|
||||||
|
})
|
||||||
match res {
|
// then commit
|
||||||
Ok(output) => {
|
.and_then(|out| {
|
||||||
|
self.storage.commit_rust_trx()?;
|
||||||
|
Ok(out)
|
||||||
|
})
|
||||||
|
// finalize undo step
|
||||||
|
.map(|output| {
|
||||||
let changes = if have_op {
|
let changes = if have_op {
|
||||||
let changes = self.op_changes();
|
let changes = self.op_changes();
|
||||||
self.maybe_clear_study_queues_after_op(&changes);
|
self.maybe_clear_study_queues_after_op(&changes);
|
||||||
@ -32,22 +36,21 @@ impl Collection {
|
|||||||
changes
|
changes
|
||||||
} else {
|
} else {
|
||||||
self.clear_study_queues();
|
self.clear_study_queues();
|
||||||
// dummy value, not used by transact_no_undo(). only required
|
// dummy value for transact_no_undo() case
|
||||||
// until we can migrate all the code to undoable ops
|
|
||||||
OpChanges {
|
OpChanges {
|
||||||
op: Op::SetFlag,
|
op: Op::SetFlag,
|
||||||
changes: StateChanges::default(),
|
changes: StateChanges::default(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.end_undoable_operation();
|
self.end_undoable_operation();
|
||||||
Ok(OpOutput { output, changes })
|
OpOutput { output, changes }
|
||||||
}
|
})
|
||||||
Err(err) => {
|
// roll back on error
|
||||||
|
.or_else(|err| {
|
||||||
self.discard_undo_and_study_queues();
|
self.discard_undo_and_study_queues();
|
||||||
self.storage.rollback_rust_trx()?;
|
self.storage.rollback_rust_trx()?;
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the provided closure in a transaction, rolling back if
|
/// Execute the provided closure in a transaction, rolling back if
|
||||||
|
@ -79,7 +79,7 @@ impl Collection {
|
|||||||
self.transact(Op::UpdateConfig, |col| {
|
self.transact(Op::UpdateConfig, |col| {
|
||||||
col.set_config(key, &value)?;
|
col.set_config(key, &value)?;
|
||||||
if !undoable {
|
if !undoable {
|
||||||
col.clear_current_undo_step_changes();
|
col.clear_current_undo_step_changes()?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -79,7 +79,7 @@ impl Collection {
|
|||||||
self.transact(Op::UpdateConfig, |col| {
|
self.transact(Op::UpdateConfig, |col| {
|
||||||
col.set_config(key, val)?;
|
col.set_config(key, val)?;
|
||||||
if !undoable {
|
if !undoable {
|
||||||
col.clear_current_undo_step_changes();
|
col.clear_current_undo_step_changes()?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -32,7 +32,7 @@ impl Collection {
|
|||||||
self.transact(Op::UpdateConfig, |col| {
|
self.transact(Op::UpdateConfig, |col| {
|
||||||
col.set_config_string_inner(key, val)?;
|
col.set_config_string_inner(key, val)?;
|
||||||
if !undoable {
|
if !undoable {
|
||||||
col.clear_current_undo_step_changes();
|
col.clear_current_undo_step_changes()?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -96,6 +96,7 @@ pub struct StateChanges {
|
|||||||
pub notetype: bool,
|
pub notetype: bool,
|
||||||
pub config: bool,
|
pub config: bool,
|
||||||
pub deck_config: bool,
|
pub deck_config: bool,
|
||||||
|
pub mtime: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -125,7 +126,7 @@ impl OpChanges {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn had_change(&self) -> bool {
|
pub fn had_change(&self) -> bool {
|
||||||
let c = &self.changes;
|
let c = &self.changes;
|
||||||
c.card || c.config || c.deck || c.deck_config || c.note || c.notetype || c.tag
|
c.card || c.config || c.deck || c.deck_config || c.note || c.notetype || c.tag || c.mtime
|
||||||
}
|
}
|
||||||
// These routines should return true even if the GUI may have
|
// These routines should return true even if the GUI may have
|
||||||
// special handling for an action, since we need to do the right
|
// special handling for an action, since we need to do the right
|
||||||
|
@ -9,7 +9,6 @@ pub(crate) use changes::UndoableChange;
|
|||||||
|
|
||||||
pub use crate::ops::Op;
|
pub use crate::ops::Op;
|
||||||
use crate::{
|
use crate::{
|
||||||
collection::undo::UndoableCollectionChange,
|
|
||||||
ops::{OpChanges, StateChanges},
|
ops::{OpChanges, StateChanges},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
@ -25,16 +24,9 @@ pub(crate) struct UndoableOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl UndoableOp {
|
impl UndoableOp {
|
||||||
/// True if changes empty, or only the collection mtime has changed.
|
/// True if changes non-empty, or a custom undo step.
|
||||||
/// Always true in the case of custom steps.
|
|
||||||
fn has_changes(&self) -> bool {
|
fn has_changes(&self) -> bool {
|
||||||
matches!(self.kind, Op::Custom(_))
|
!self.changes.is_empty() || matches!(self.kind, Op::Custom(_))
|
||||||
|| !matches!(
|
|
||||||
&self.changes[..],
|
|
||||||
&[] | &[UndoableChange::Collection(
|
|
||||||
UndoableCollectionChange::Modified(_)
|
|
||||||
)]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,9 +254,14 @@ impl Collection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Forget any recorded changes in the current transaction, allowing
|
/// Forget any recorded changes in the current transaction, allowing
|
||||||
/// a minor change like a config update to bypass undo.
|
/// a minor change like a config update to bypass undo. Bumps mtime if
|
||||||
pub(crate) fn clear_current_undo_step_changes(&mut self) {
|
/// there were pending changes.
|
||||||
self.state.undo.clear_current_changes()
|
pub(crate) fn clear_current_undo_step_changes(&mut self) -> Result<()> {
|
||||||
|
if self.current_undo_step_has_changes() {
|
||||||
|
self.set_modified()?;
|
||||||
|
}
|
||||||
|
self.state.undo.clear_current_changes();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn current_undo_op(&self) -> Option<&UndoableOp> {
|
pub(crate) fn current_undo_op(&self) -> Option<&UndoableOp> {
|
||||||
@ -275,6 +272,18 @@ impl Collection {
|
|||||||
self.state.undo.previous_op()
|
self.state.undo.previous_op()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn undoing_or_redoing(&self) -> bool {
|
||||||
|
self.state.undo.mode != UndoMode::NormalOp
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn current_undo_step_has_changes(&self) -> bool {
|
||||||
|
self.state
|
||||||
|
.undo
|
||||||
|
.current_op()
|
||||||
|
.map(|op| op.has_changes())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
/// Used for coalescing successive note updates.
|
/// Used for coalescing successive note updates.
|
||||||
pub(crate) fn pop_last_change(&mut self) -> Option<UndoableChange> {
|
pub(crate) fn pop_last_change(&mut self) -> Option<UndoableChange> {
|
||||||
self.state
|
self.state
|
||||||
@ -317,6 +326,9 @@ impl Collection {
|
|||||||
impl From<&[UndoableChange]> for StateChanges {
|
impl From<&[UndoableChange]> for StateChanges {
|
||||||
fn from(changes: &[UndoableChange]) -> Self {
|
fn from(changes: &[UndoableChange]) -> Self {
|
||||||
let mut out = StateChanges::default();
|
let mut out = StateChanges::default();
|
||||||
|
if !changes.is_empty() {
|
||||||
|
out.mtime = true;
|
||||||
|
}
|
||||||
for change in changes {
|
for change in changes {
|
||||||
match change {
|
match change {
|
||||||
UndoableChange::Card(_) => out.card = true,
|
UndoableChange::Card(_) => out.card = true,
|
||||||
@ -511,4 +523,42 @@ mod test {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn undo_mtime_bump() -> Result<()> {
|
||||||
|
let mut col = open_test_collection();
|
||||||
|
let mtime = col.storage.get_collection_timestamps()?.collection_change;
|
||||||
|
|
||||||
|
// a no-op change should not bump mtime
|
||||||
|
let out = col.set_config_bool(BoolKey::AddingDefaultsToCurrentDeck, true, true)?;
|
||||||
|
assert_eq!(
|
||||||
|
mtime,
|
||||||
|
col.storage.get_collection_timestamps()?.collection_change
|
||||||
|
);
|
||||||
|
assert_eq!(out.changes.had_change(), false);
|
||||||
|
|
||||||
|
// if there is an undoable step, mtime should change
|
||||||
|
let out = col.set_config_bool(BoolKey::AddingDefaultsToCurrentDeck, false, true)?;
|
||||||
|
let new_mtime = col.storage.get_collection_timestamps()?.collection_change;
|
||||||
|
assert_ne!(mtime, new_mtime);
|
||||||
|
assert_eq!(out.changes.had_change(), true);
|
||||||
|
|
||||||
|
// when skipping undo, mtime should still only be bumped on a change
|
||||||
|
let out = col.set_config_bool(BoolKey::AddingDefaultsToCurrentDeck, false, false)?;
|
||||||
|
assert_eq!(
|
||||||
|
new_mtime,
|
||||||
|
col.storage.get_collection_timestamps()?.collection_change
|
||||||
|
);
|
||||||
|
assert_eq!(out.changes.had_change(), false);
|
||||||
|
|
||||||
|
// op output won't reflect changes were made
|
||||||
|
let out = col.set_config_bool(BoolKey::AddingDefaultsToCurrentDeck, true, false)?;
|
||||||
|
assert_ne!(
|
||||||
|
new_mtime,
|
||||||
|
col.storage.get_collection_timestamps()?.collection_change
|
||||||
|
);
|
||||||
|
assert_eq!(out.changes.had_change(), false);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user