mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
add LIFO sorting options for new cards
This commit is contained in:
parent
afaaa763ec
commit
410660990e
7 changed files with 67 additions and 34 deletions
|
@ -97,18 +97,21 @@ deck-config-new-gather-priority-tooltip =
|
||||||
to prioritize subdecks that are closer to the top.
|
to prioritize subdecks that are closer to the top.
|
||||||
|
|
||||||
`Position`: gathers cards from all decks before they are sorted. This
|
`Position`: gathers cards from all decks before they are sorted. This
|
||||||
ensures the oldest cards will be shown first, even if the parent limit is
|
ensures cards appear in strict position order, even if the parent limit is
|
||||||
not high enough to see cards from all decks.
|
not high enough to see cards from all decks.
|
||||||
deck-config-new-gather-priority-deck = Deck
|
deck-config-new-gather-priority-deck = Deck
|
||||||
deck-config-new-gather-priority-position = Position
|
deck-config-new-gather-priority-position-lowest-first = Position (lowest first)
|
||||||
|
deck-config-new-gather-priority-position-highest-first = Position (highest first)
|
||||||
deck-config-new-card-sort-order = New card sort order
|
deck-config-new-card-sort-order = New card sort order
|
||||||
deck-config-new-card-sort-order-tooltip =
|
deck-config-new-card-sort-order-tooltip =
|
||||||
How cards are sorted after they have been gathered. By default, Anki sorts
|
How cards are sorted after they have been gathered. By default, Anki sorts
|
||||||
by card template first, to avoid multiple cards of the same note from being
|
by card template first, to avoid multiple cards of the same note from being
|
||||||
shown in succession.
|
shown in succession.
|
||||||
deck-config-sort-order-card-template-then-position = Card template, then position
|
deck-config-sort-order-card-template-then-lowest-position = Card template, then lowest position
|
||||||
|
deck-config-sort-order-card-template-then-highest-position = Card template, then highest position
|
||||||
deck-config-sort-order-card-template-then-random = Card template, then random
|
deck-config-sort-order-card-template-then-random = Card template, then random
|
||||||
deck-config-sort-order-position = Position (siblings together)
|
deck-config-sort-order-lowest-position = Lowest position
|
||||||
|
deck-config-sort-order-highest-position = Highest position
|
||||||
deck-config-sort-order-random = Random
|
deck-config-sort-order-random = Random
|
||||||
deck-config-new-review-priority = New/review priority
|
deck-config-new-review-priority = New/review priority
|
||||||
deck-config-new-review-priority-tooltip = When to show new cards in relation to review cards.
|
deck-config-new-review-priority-tooltip = When to show new cards in relation to review cards.
|
||||||
|
|
|
@ -320,13 +320,16 @@ message DeckConfig {
|
||||||
}
|
}
|
||||||
enum NewCardGatherPriority {
|
enum NewCardGatherPriority {
|
||||||
NEW_CARD_GATHER_PRIORITY_DECK = 0;
|
NEW_CARD_GATHER_PRIORITY_DECK = 0;
|
||||||
NEW_CARD_GATHER_PRIORITY_POSITION = 1;
|
NEW_CARD_GATHER_PRIORITY_LOWEST_POSITION = 1;
|
||||||
|
NEW_CARD_GATHER_PRIORITY_HIGHEST_POSITION = 2;
|
||||||
}
|
}
|
||||||
enum NewCardSortOrder {
|
enum NewCardSortOrder {
|
||||||
NEW_CARD_SORT_ORDER_TEMPLATE_THEN_DUE = 0;
|
NEW_CARD_SORT_ORDER_TEMPLATE_THEN_LOWEST_POSITION = 0;
|
||||||
NEW_CARD_SORT_ORDER_TEMPLATE_THEN_RANDOM = 1;
|
NEW_CARD_SORT_ORDER_TEMPLATE_THEN_HIGHEST_POSITION = 1;
|
||||||
NEW_CARD_SORT_ORDER_DUE = 2;
|
NEW_CARD_SORT_ORDER_TEMPLATE_THEN_RANDOM = 2;
|
||||||
NEW_CARD_SORT_ORDER_RANDOM = 3;
|
NEW_CARD_SORT_ORDER_LOWEST_POSITION = 3;
|
||||||
|
NEW_CARD_SORT_ORDER_HIGHEST_POSITION = 4;
|
||||||
|
NEW_CARD_SORT_ORDER_RANDOM = 5;
|
||||||
}
|
}
|
||||||
enum ReviewCardOrder {
|
enum ReviewCardOrder {
|
||||||
REVIEW_CARD_ORDER_DAY = 0;
|
REVIEW_CARD_ORDER_DAY = 0;
|
||||||
|
|
|
@ -63,7 +63,7 @@ impl Default for DeckConfig {
|
||||||
graduating_interval_easy: 4,
|
graduating_interval_easy: 4,
|
||||||
new_card_insert_order: NewCardInsertOrder::Due as i32,
|
new_card_insert_order: NewCardInsertOrder::Due as i32,
|
||||||
new_card_gather_priority: NewCardGatherPriority::Deck as i32,
|
new_card_gather_priority: NewCardGatherPriority::Deck as i32,
|
||||||
new_card_sort_order: NewCardSortOrder::TemplateThenDue as i32,
|
new_card_sort_order: NewCardSortOrder::TemplateThenLowestPosition as i32,
|
||||||
review_order: ReviewCardOrder::Day as i32,
|
review_order: ReviewCardOrder::Day as i32,
|
||||||
new_mix: ReviewMix::MixWithReviews as i32,
|
new_mix: ReviewMix::MixWithReviews as i32,
|
||||||
interday_learning_mix: ReviewMix::MixWithReviews as i32,
|
interday_learning_mix: ReviewMix::MixWithReviews as i32,
|
||||||
|
|
|
@ -209,7 +209,7 @@ impl Collection {
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
// filtered decks do not space siblings
|
// filtered decks do not space siblings
|
||||||
QueueSortOptions {
|
QueueSortOptions {
|
||||||
new_order: NewCardSortOrder::Due,
|
new_order: NewCardSortOrder::LowestPosition,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -272,25 +272,28 @@ impl Collection {
|
||||||
}
|
}
|
||||||
selected_deck_limits.new = selected_deck_limits.new.min(selected_deck_limits.review);
|
selected_deck_limits.new = selected_deck_limits.new.min(selected_deck_limits.review);
|
||||||
let can_exit_early = sort_options.new_gather_priority == NewCardGatherPriority::Deck;
|
let can_exit_early = sort_options.new_gather_priority == NewCardGatherPriority::Deck;
|
||||||
|
let reverse = sort_options.new_gather_priority == NewCardGatherPriority::HighestPosition;
|
||||||
for deck in &decks {
|
for deck in &decks {
|
||||||
if can_exit_early && selected_deck_limits.new == 0 {
|
if can_exit_early && selected_deck_limits.new == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let limit = remaining.get_mut(&deck.id).unwrap();
|
let limit = remaining.get_mut(&deck.id).unwrap();
|
||||||
if limit.new > 0 {
|
if limit.new > 0 {
|
||||||
self.storage.for_each_new_card_in_deck(deck.id, |card| {
|
self.storage
|
||||||
let bury = get_bury_mode(card.original_deck_id.or(deck.id));
|
.for_each_new_card_in_deck(deck.id, reverse, |card| {
|
||||||
if limit.new != 0 {
|
let bury = get_bury_mode(card.original_deck_id.or(deck.id));
|
||||||
if queues.add_new_card(card, bury) {
|
if limit.new != 0 {
|
||||||
limit.new -= 1;
|
if queues.add_new_card(card, bury) {
|
||||||
selected_deck_limits.new = selected_deck_limits.new.saturating_sub(1);
|
limit.new -= 1;
|
||||||
}
|
selected_deck_limits.new =
|
||||||
|
selected_deck_limits.new.saturating_sub(1);
|
||||||
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,18 @@ use super::{NewCard, NewCardSortOrder, QueueBuilder};
|
||||||
impl QueueBuilder {
|
impl QueueBuilder {
|
||||||
pub(super) fn sort_new(&mut self) {
|
pub(super) fn sort_new(&mut self) {
|
||||||
match self.sort_options.new_order {
|
match self.sort_options.new_order {
|
||||||
NewCardSortOrder::TemplateThenDue => {
|
NewCardSortOrder::TemplateThenLowestPosition => {
|
||||||
self.new.sort_unstable_by(template_then_due);
|
self.new.sort_unstable_by(template_then_lowest_position);
|
||||||
|
}
|
||||||
|
NewCardSortOrder::TemplateThenHighestPosition => {
|
||||||
|
self.new.sort_unstable_by(template_then_highest_position);
|
||||||
}
|
}
|
||||||
NewCardSortOrder::TemplateThenRandom => {
|
NewCardSortOrder::TemplateThenRandom => {
|
||||||
self.new.iter_mut().for_each(NewCard::hash_id_and_mtime);
|
self.new.iter_mut().for_each(NewCard::hash_id_and_mtime);
|
||||||
self.new.sort_unstable_by(template_then_random);
|
self.new.sort_unstable_by(template_then_random);
|
||||||
}
|
}
|
||||||
NewCardSortOrder::Due => self.new.sort_unstable_by(new_position),
|
NewCardSortOrder::LowestPosition => self.new.sort_unstable_by(lowest_position),
|
||||||
|
NewCardSortOrder::HighestPosition => self.new.sort_unstable_by(highest_position),
|
||||||
NewCardSortOrder::Random => {
|
NewCardSortOrder::Random => {
|
||||||
self.new.iter_mut().for_each(NewCard::hash_id_and_mtime);
|
self.new.iter_mut().for_each(NewCard::hash_id_and_mtime);
|
||||||
self.new.sort_unstable_by(new_hash)
|
self.new.sort_unstable_by(new_hash)
|
||||||
|
@ -26,18 +30,26 @@ impl QueueBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn template_then_due(a: &NewCard, b: &NewCard) -> Ordering {
|
fn template_then_lowest_position(a: &NewCard, b: &NewCard) -> Ordering {
|
||||||
(a.template_index, a.due).cmp(&(b.template_index, b.due))
|
(a.template_index, a.due).cmp(&(b.template_index, b.due))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn template_then_highest_position(a: &NewCard, b: &NewCard) -> Ordering {
|
||||||
|
(a.template_index, b.due).cmp(&(b.template_index, a.due))
|
||||||
|
}
|
||||||
|
|
||||||
fn template_then_random(a: &NewCard, b: &NewCard) -> Ordering {
|
fn template_then_random(a: &NewCard, b: &NewCard) -> Ordering {
|
||||||
(a.template_index, a.hash).cmp(&(b.template_index, b.hash))
|
(a.template_index, a.hash).cmp(&(b.template_index, b.hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_position(a: &NewCard, b: &NewCard) -> Ordering {
|
fn lowest_position(a: &NewCard, b: &NewCard) -> Ordering {
|
||||||
a.due.cmp(&b.due)
|
a.due.cmp(&b.due)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn highest_position(a: &NewCard, b: &NewCard) -> Ordering {
|
||||||
|
b.due.cmp(&a.due)
|
||||||
|
}
|
||||||
|
|
||||||
fn new_hash(a: &NewCard, b: &NewCard) -> Ordering {
|
fn new_hash(a: &NewCard, b: &NewCard) -> Ordering {
|
||||||
a.hash.cmp(&b.hash)
|
a.hash.cmp(&b.hash)
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,12 +229,21 @@ impl super::SqliteStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call func() for each new card, stopping when it returns false
|
/// Call func() for each new card, stopping when it returns false
|
||||||
/// or no more cards found. Cards will arrive in (deck_id, due) order.
|
/// or no more cards found.
|
||||||
pub(crate) fn for_each_new_card_in_deck<F>(&self, deck: DeckId, mut func: F) -> Result<()>
|
pub(crate) fn for_each_new_card_in_deck<F>(
|
||||||
|
&self,
|
||||||
|
deck: DeckId,
|
||||||
|
reverse: bool,
|
||||||
|
mut func: F,
|
||||||
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
F: FnMut(NewCard) -> bool,
|
F: FnMut(NewCard) -> bool,
|
||||||
{
|
{
|
||||||
let mut stmt = self.db.prepare_cached(include_str!("new_cards.sql"))?;
|
let mut stmt = self.db.prepare_cached(&format!(
|
||||||
|
"{}{}",
|
||||||
|
include_str!("new_cards.sql"),
|
||||||
|
if reverse { " order by due desc" } else { "" }
|
||||||
|
))?;
|
||||||
let mut rows = stmt.query(params![deck])?;
|
let mut rows = stmt.query(params![deck])?;
|
||||||
while let Some(row) = rows.next()? {
|
while let Some(row) = rows.next()? {
|
||||||
if !func(NewCard {
|
if !func(NewCard {
|
||||||
|
|
|
@ -15,12 +15,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
const newGatherPriorityChoices = [
|
const newGatherPriorityChoices = [
|
||||||
tr.deckConfigNewGatherPriorityDeck(),
|
tr.deckConfigNewGatherPriorityDeck(),
|
||||||
tr.deckConfigNewGatherPriorityPosition(),
|
tr.deckConfigNewGatherPriorityPositionLowestFirst(),
|
||||||
|
tr.deckConfigNewGatherPriorityPositionHighestFirst(),
|
||||||
];
|
];
|
||||||
const newSortOrderChoices = [
|
const newSortOrderChoices = [
|
||||||
tr.deckConfigSortOrderCardTemplateThenPosition(),
|
tr.deckConfigSortOrderCardTemplateThenLowestPosition(),
|
||||||
|
tr.deckConfigSortOrderCardTemplateThenHighestPosition(),
|
||||||
tr.deckConfigSortOrderCardTemplateThenRandom(),
|
tr.deckConfigSortOrderCardTemplateThenRandom(),
|
||||||
tr.deckConfigSortOrderPosition(),
|
tr.deckConfigSortOrderLowestPosition(),
|
||||||
|
tr.deckConfigSortOrderHighestPosition(),
|
||||||
tr.deckConfigSortOrderRandom(),
|
tr.deckConfigSortOrderRandom(),
|
||||||
];
|
];
|
||||||
const reviewOrderChoices = [
|
const reviewOrderChoices = [
|
||||||
|
|
Loading…
Reference in a new issue