Revert bury restriction (#2432)

* Remove outdated comment.

* Revert removal of independent bury rules

* Revert 'hierarchical bury modes'

It's now again allowed to bury new, but not review cards e.g., but
siblings of previously gathered card queues will not be buried.

* Tweak docs (dae)

* Add missing Learn and PreviewRepeat queues
This commit is contained in:
RumovZ 2023-03-11 08:49:18 +01:00 committed by GitHub
parent 9b5148185a
commit c4d5eeb7a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 130 additions and 96 deletions

View file

@ -104,22 +104,28 @@ deck-config-leech-action-tooltip =
## Burying section ## Burying section
deck-config-bury-title = Burying deck-config-bury-title = Burying
deck-config-bury-siblings = Bury siblings deck-config-bury-new-siblings = Bury new siblings
deck-config-do-not-bury = Do not bury siblings deck-config-bury-review-siblings = Bury review siblings
deck-config-bury-if-new = Bury if new deck-config-bury-interday-learning-siblings = Bury interday learning siblings
deck-config-bury-if-new-or-review = Bury if new or review deck-config-bury-new-tooltip =
deck-config-bury-if-new-review-or-interday = Bury if new, review, or interday learning Whether other `new` cards of the same note (eg reverse cards, adjacent cloze deletions)
deck-config-bury-tooltip = will be delayed until the next day.
Siblings are other cards from the same note (eg forward/reverse cards, or deck-config-bury-review-tooltip = Whether other `review` cards of the same note will be delayed until the next day.
other cloze deletions from the same text). deck-config-bury-interday-learning-tooltip =
Whether other `learning` cards of the same note with intervals > 1 day
will be delayed until the next day.
deck-config-bury-priority-tooltip =
When Anki gathers cards, it first gathers intraday learning cards, then
interday learning cards, then reviews, and finally new cards. This affects
how burying works:
When this option is off, multiple cards from the same note may be seen on the same - If you have all burying options enabled, the sibling that comes earliest in
day. When enabled, Anki will automatically *bury* siblings, hiding them until the next that list will be shown. For example, a review card will be shown in preference
day. This option allows you to choose which kinds of cards may be buried when you answer to a new card.
one of their siblings. - Siblings later in the list can not bury earlier card types. For example, if you
disable burying of new cards, and study a new card, it will not bury any interday
When using the V3 scheduler, interday learning cards can also be buried. Interday learning or review cards, and you may see both a review sibling and new sibling in the
learning cards are cards with a current learning step of one or more days. same session.
## Ordering section ## Ordering section
@ -304,13 +310,19 @@ deck-config-which-deck = Which deck would you like?
## NO NEED TO TRANSLATE. This text is no longer used by Anki, and will be removed in the future. ## NO NEED TO TRANSLATE. This text is no longer used by Anki, and will be removed in the future.
deck-config-bury-new-siblings = Bury new siblings deck-config-bury-siblings = Bury siblings
deck-config-bury-review-siblings = Bury review siblings deck-config-do-not-bury = Do not bury siblings
deck-config-bury-interday-learning-siblings = Bury interday learning siblings deck-config-bury-if-new = Bury if new
deck-config-bury-new-tooltip = deck-config-bury-if-new-or-review = Bury if new or review
Whether other `new` cards of the same note (eg reverse cards, adjacent cloze deletions) deck-config-bury-if-new-review-or-interday = Bury if new, review, or interday learning
will be delayed until the next day. deck-config-bury-tooltip =
deck-config-bury-review-tooltip = Whether other `review` cards of the same note will be delayed until the next day. Siblings are other cards from the same note (eg forward/reverse cards, or
deck-config-bury-interday-learning-tooltip = other cloze deletions from the same text).
Whether other `learning` cards of the same note with intervals > 1 day
will be delayed until the next day. When this option is off, multiple cards from the same note may be seen on the same
day. When enabled, Anki will automatically *bury* siblings, hiding them until the next
day. This option allows you to choose which kinds of cards may be buried when you answer
one of their siblings.
When using the V3 scheduler, interday learning cards can also be buried. Interday
learning cards are cards with a current learning step of one or more days.

View file

@ -124,13 +124,8 @@ message DeckConfig {
bool show_timer = 25; bool show_timer = 25;
bool skip_question_when_replaying_answer = 26; bool skip_question_when_replaying_answer = 26;
// the new scheduler doesn't allow toggling these booleans freely anymore,
// but they are continued to be used for reasons of backwards compatibility
bool bury_new = 27; bool bury_new = 27;
// only respected if bury_new
bool bury_reviews = 28; bool bury_reviews = 28;
// only respected if bury_new and bury_reviews
bool bury_interday_learning = 29; bool bury_interday_learning = 29;
bytes other = 255; bytes other = 255;

View file

@ -288,7 +288,7 @@ impl Collection {
fn maybe_bury_siblings(&mut self, card: &Card, config: &DeckConfig) -> Result<()> { fn maybe_bury_siblings(&mut self, card: &Card, config: &DeckConfig) -> Result<()> {
let bury_mode = BuryMode::from_deck_config(config); let bury_mode = BuryMode::from_deck_config(config);
if bury_mode.any_burying() { if bury_mode.any_burying() {
self.bury_siblings(card.id, card.note_id, bury_mode)?; self.bury_siblings(card, card.note_id, bury_mode)?;
} }
Ok(()) Ok(())
} }

View file

@ -80,7 +80,6 @@ impl Collection {
}) })
} }
/// Bury/suspend cards in search table, and clear it.
/// Marks the cards as modified. /// Marks the cards as modified.
fn bury_or_suspend_cards_inner( fn bury_or_suspend_cards_inner(
&mut self, &mut self,
@ -139,15 +138,39 @@ impl Collection {
pub(crate) fn bury_siblings( pub(crate) fn bury_siblings(
&mut self, &mut self,
cid: CardId, card: &Card,
nid: NoteId, nid: NoteId,
bury_mode: BuryMode, mut bury_mode: BuryMode,
) -> Result<usize> { ) -> Result<usize> {
let cards = self.storage.all_siblings_for_bury(cid, nid, bury_mode)?; bury_mode.exclude_earlier_gathered_queues(card.queue);
let cards = self
.storage
.all_siblings_for_bury(card.id, nid, bury_mode)?;
self.bury_or_suspend_cards_inner(cards, BuryOrSuspendMode::BurySched) self.bury_or_suspend_cards_inner(cards, BuryOrSuspendMode::BurySched)
} }
} }
impl BuryMode {
/// Disables burying for queues gathered before `queue`.
fn exclude_earlier_gathered_queues(&mut self, queue: CardQueue) {
self.bury_interday_learning &= queue.gather_ord() <= CardQueue::DayLearn.gather_ord();
self.bury_reviews &= queue.gather_ord() <= CardQueue::Review.gather_ord();
}
}
impl CardQueue {
fn gather_ord(self) -> u8 {
match self {
CardQueue::Learn | CardQueue::PreviewRepeat => 0,
CardQueue::DayLearn => 1,
CardQueue::Review => 2,
CardQueue::New => 3,
// not gathered
CardQueue::Suspended | CardQueue::SchedBuried | CardQueue::UserBuried => u8::MAX,
}
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::card::Card; use crate::card::Card;

View file

@ -56,15 +56,10 @@ impl Context {
impl BuryMode { impl BuryMode {
pub(crate) fn from_deck_config(config: &DeckConfig) -> BuryMode { pub(crate) fn from_deck_config(config: &DeckConfig) -> BuryMode {
let cfg = &config.inner; let cfg = &config.inner;
// Since cards are gathered in a certain order (learning > review > new) and
// a card can only bury siblings that are gathered later, only the four bury
// modes following this order are allowed.
// Booleans are continued to be used for reasons of backwards compatibility.
// https://github.com/ankitects/anki/issues/2352
BuryMode { BuryMode {
bury_new: cfg.bury_new, bury_new: cfg.bury_new,
bury_reviews: cfg.bury_new && cfg.bury_reviews, bury_reviews: cfg.bury_reviews,
bury_interday_learning: cfg.bury_new && cfg.bury_reviews && cfg.bury_interday_learning, bury_interday_learning: cfg.bury_interday_learning,
} }
} }

View file

@ -456,7 +456,7 @@ mod test {
#[test] #[test]
fn new_card_potentially_burying_review_card() { fn new_card_potentially_burying_review_card() {
let mut col = open_test_collection(); let mut col = Collection::new_v3();
// add one new and one review card // add one new and one review card
CardAdder::new().siblings(2).due_dates(["0"]).add(&mut col); CardAdder::new().siblings(2).due_dates(["0"]).add(&mut col);
// Potentially problematic config: New cards are shown first and would bury // Potentially problematic config: New cards are shown first and would bury

View file

@ -3,7 +3,6 @@ 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
--> -->
<script lang="ts"> <script lang="ts">
import type { anki } from "@tslib/backend_proto";
import * as tr from "@tslib/ftl"; import * as tr from "@tslib/ftl";
import type Carousel from "bootstrap/js/dist/carousel"; import type Carousel from "bootstrap/js/dist/carousel";
import type Modal from "bootstrap/js/dist/modal"; import type Modal from "bootstrap/js/dist/modal";
@ -11,10 +10,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import DynamicallySlottable from "../components/DynamicallySlottable.svelte"; import DynamicallySlottable from "../components/DynamicallySlottable.svelte";
import Item from "../components/Item.svelte"; import Item from "../components/Item.svelte";
import TitledContainer from "../components/TitledContainer.svelte"; import TitledContainer from "../components/TitledContainer.svelte";
import EnumSelectorRow from "./EnumSelectorRow.svelte";
import HelpModal from "./HelpModal.svelte"; import HelpModal from "./HelpModal.svelte";
import type { DeckOptionsState } from "./lib"; import type { DeckOptionsState } from "./lib";
import SettingTitle from "./SettingTitle.svelte"; import SettingTitle from "./SettingTitle.svelte";
import SwitchRow from "./SwitchRow.svelte";
import type { DeckOption } from "./types"; import type { DeckOption } from "./types";
export let state: DeckOptionsState; export let state: DeckOptionsState;
@ -23,54 +22,33 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const config = state.currentConfig; const config = state.currentConfig;
const defaults = state.defaults; const defaults = state.defaults;
const burySiblings: DeckOption = { const priorityTooltip = state.v3Scheduler
title: tr.deckConfigBurySiblings(), ? "\n\n" + tr.deckConfigBuryPriorityTooltip()
help: tr.deckConfigBuryTooltip(), : "";
const settings = {
buryNewSiblings: {
title: tr.deckConfigBuryNewSiblings(),
help: tr.deckConfigBuryNewTooltip() + priorityTooltip,
},
buryReviewSiblings: {
title: tr.deckConfigBuryReviewSiblings(),
help: tr.deckConfigBuryReviewTooltip() + priorityTooltip,
},
buryInterdayLearningSiblings: {
title: tr.deckConfigBuryInterdayLearningSiblings(),
help: tr.deckConfigBuryInterdayLearningTooltip() + priorityTooltip,
},
}; };
const helpSections = Object.values(settings) as DeckOption[];
let modal: Modal; let modal: Modal;
let carousel: Carousel; let carousel: Carousel;
function openHelpModal(): void { function openHelpModal(index: number): void {
modal.show(); modal.show();
carousel.to(0); carousel.to(index);
} }
const enum BuryMode {
NONE,
NEW,
NEW_REVIEW,
NEW_REVIEW_LEARNING,
}
const buryModeChoices = [
tr.deckConfigDoNotBury(),
tr.deckConfigBuryIfNew(),
tr.deckConfigBuryIfNewOrReview(),
];
if (state.v3Scheduler) {
buryModeChoices.push(tr.deckConfigBuryIfNewReviewOrInterday());
}
function buryModeFromConfig(config: anki.deckconfig.DeckConfig.Config): BuryMode {
// burying review cards is only allowed if also burying new cards
const buryReviews = config.buryNew && config.buryReviews;
// burying learning cards is only allowed if also burying review and new cards
const buryInterdayLearning =
buryReviews && config.buryInterdayLearning && state.v3Scheduler;
return (
Number(config.buryNew) + Number(buryReviews) + Number(buryInterdayLearning)
);
}
function buryModeToConfig(mode: BuryMode) {
$config.buryNew = mode >= 1;
$config.buryReviews = mode >= 2;
$config.buryInterdayLearning = mode >= 3;
}
let mode = buryModeFromConfig($config);
$: buryModeToConfig(mode);
</script> </script>
<TitledContainer title={tr.deckConfigBuryTitle()}> <TitledContainer title={tr.deckConfigBuryTitle()}>
@ -78,7 +56,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
title={tr.deckConfigBuryTitle()} title={tr.deckConfigBuryTitle()}
url="https://docs.ankiweb.net/studying.html#siblings-and-burying" url="https://docs.ankiweb.net/studying.html#siblings-and-burying"
slot="tooltip" slot="tooltip"
helpSections={[burySiblings]} {helpSections}
on:mount={(e) => { on:mount={(e) => {
modal = e.detail.modal; modal = e.detail.modal;
carousel = e.detail.carousel; carousel = e.detail.carousel;
@ -86,15 +64,46 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
/> />
<DynamicallySlottable slotHost={Item} {api}> <DynamicallySlottable slotHost={Item} {api}>
<Item> <Item>
<EnumSelectorRow <SwitchRow bind:value={$config.buryNew} defaultValue={defaults.buryNew}>
bind:value={mode} <SettingTitle
defaultValue={buryModeFromConfig(defaults)} on:click={() =>
choices={buryModeChoices} openHelpModal(Object.keys(settings).indexOf("buryNewSiblings"))}
>{settings.buryNewSiblings.title}</SettingTitle
> >
<SettingTitle on:click={() => openHelpModal()} </SwitchRow>
>{tr.deckConfigBurySiblings()}</SettingTitle
>
</EnumSelectorRow>
</Item> </Item>
<Item>
<SwitchRow
bind:value={$config.buryReviews}
defaultValue={defaults.buryReviews}
>
<SettingTitle
on:click={() =>
openHelpModal(
Object.keys(settings).indexOf("buryReviewSiblings"),
)}>{settings.buryReviewSiblings.title}</SettingTitle
>
</SwitchRow>
</Item>
{#if state.v3Scheduler}
<Item>
<SwitchRow
bind:value={$config.buryInterdayLearning}
defaultValue={defaults.buryInterdayLearning}
>
<SettingTitle
on:click={() =>
openHelpModal(
Object.keys(settings).indexOf(
"buryInterdayLearningSiblings",
),
)}
>{settings.buryInterdayLearningSiblings.title}</SettingTitle
>
</SwitchRow>
</Item>
{/if}
</DynamicallySlottable> </DynamicallySlottable>
</TitledContainer> </TitledContainer>