diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 2f3b32626..5f08aa9ab 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -12,7 +12,7 @@ SearchNode = _pb.SearchNode Progress = _pb.Progress EmptyCardsReport = _pb.EmptyCardsReport GraphPreferences = _pb.GraphPreferences -BuiltinSort = _pb.SortOrder.Builtin +BuiltinSort = _pb.SortOrder.Builtin.SortColumn Preferences = _pb.Preferences UndoStatus = _pb.UndoStatus OpChanges = _pb.OpChanges @@ -506,7 +506,7 @@ class Collection: def find_cards( self, query: str, - order: Union[bool, str, BuiltinSort.Kind.V] = False, + order: Union[bool, str, BuiltinSort.V] = False, reverse: bool = False, ) -> Sequence[CardId]: """Return card ids matching the provided search. @@ -521,10 +521,10 @@ class Collection: desc and vice versa when reverse is set in the collection config, eg order="c.ivl asc, c.due desc". - If order is a BuiltinSort.Kind value, sort using that builtin sort, eg - col.find_cards("", order=BuiltinSort.Kind.DUE) + If order is a BuiltinSort value, sort using that builtin sort, eg + col.find_cards("", order=BuiltinSort.DUE) - The reverse argument only applies when a BuiltinSort.Kind is provided; + The reverse argument only applies when a BuiltinSort.V is provided; otherwise the collection config defines whether reverse is set or not. """ mode = _build_sort_mode(order, reverse) @@ -535,7 +535,7 @@ class Collection: def find_notes( self, query: str, - order: Union[bool, str, BuiltinSort.Kind.V] = False, + order: Union[bool, str, BuiltinSort.V] = False, reverse: bool = False, ) -> Sequence[NoteId]: """Return note ids matching the provided search. @@ -1123,7 +1123,7 @@ _UndoInfo = Union[_ReviewsUndo, LegacyCheckpoint, None] def _build_sort_mode( - order: Union[bool, str, BuiltinSort.Kind.V], + order: Union[bool, str, BuiltinSort.V], reverse: bool, ) -> _pb.SortOrder: if isinstance(order, str): @@ -1134,4 +1134,4 @@ def _build_sort_mode( else: return _pb.SortOrder(none=_pb.Empty()) else: - return _pb.SortOrder(builtin=_pb.SortOrder.Builtin(kind=order, reverse=reverse)) + return _pb.SortOrder(builtin=_pb.SortOrder.Builtin(column=order, reverse=reverse)) diff --git a/rslib/backend.proto b/rslib/backend.proto index b4479baae..b1c8989a9 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -796,28 +796,22 @@ message SearchOut { message SortOrder { message Builtin { - enum Kind { - NOTE_CARDS = 0; - NOTE_CREATION = 1; - NOTE_DUE = 2; - NOTE_EASE = 3; - SORT_FIELD = 4; - NOTE_INTERVAL = 5; - NOTE_LAPSES = 6; - NOTE_MOD = 7; - NOTE_REPS = 8; - TAGS = 9; - NOTETYPE = 10; - CARD_MOD = 11; - REPS = 12; - DUE = 13; - EASE = 14; - LAPSES = 15; - INTERVAL = 16; - DECK = 17; - CARDS = 18; + enum SortColumn { + CARD_MOD = 0; + CARDS = 1; + DECK = 2; + DUE = 3; + EASE = 4; + LAPSES = 5; + INTERVAL = 6; + NOTE_CREATION = 7; + NOTE_MOD = 8; + NOTETYPE = 9; + REPS = 10; + SORT_FIELD = 11; + TAGS = 12; } - Kind kind = 1; + SortColumn column = 1; bool reverse = 2; } oneof value { diff --git a/rslib/src/backend/search/mod.rs b/rslib/src/backend/search/mod.rs index e5285d6f2..dcbda879c 100644 --- a/rslib/src/backend/search/mod.rs +++ b/rslib/src/backend/search/mod.rs @@ -10,9 +10,9 @@ use super::Backend; use crate::{ backend_proto as pb, backend_proto::{ - sort_order::builtin::Kind as SortKindProto, sort_order::Value as SortOrderProto, + sort_order::builtin::SortColumn as SortColumnProto, sort_order::Value as SortOrderProto, }, - config::SortKind, + browser_table::Column, prelude::*, search::{concatenate_searches, replace_search_node, write_nodes, Node, SortMode}, }; @@ -108,28 +108,22 @@ impl SearchService for Backend { } } -impl From for SortKind { - fn from(kind: SortKindProto) -> Self { +impl From for Column { + fn from(kind: SortColumnProto) -> Self { match kind { - SortKindProto::NoteCards => SortKind::NoteCards, - SortKindProto::NoteCreation => SortKind::NoteCreation, - SortKindProto::NoteDue => SortKind::NoteDue, - SortKindProto::NoteEase => SortKind::NoteEase, - SortKindProto::NoteInterval => SortKind::NoteInterval, - SortKindProto::NoteLapses => SortKind::NoteLapses, - SortKindProto::NoteMod => SortKind::NoteMod, - SortKindProto::SortField => SortKind::SortField, - SortKindProto::NoteReps => SortKind::NoteReps, - SortKindProto::Tags => SortKind::Tags, - SortKindProto::Notetype => SortKind::Notetype, - SortKindProto::CardMod => SortKind::CardMod, - SortKindProto::Reps => SortKind::Reps, - SortKindProto::Due => SortKind::Due, - SortKindProto::Ease => SortKind::Ease, - SortKindProto::Lapses => SortKind::Lapses, - SortKindProto::Interval => SortKind::Interval, - SortKindProto::Deck => SortKind::Deck, - SortKindProto::Cards => SortKind::Cards, + SortColumnProto::CardMod => Column::CardMod, + SortColumnProto::Cards => Column::Cards, + SortColumnProto::Deck => Column::Deck, + SortColumnProto::Due => Column::Due, + SortColumnProto::Ease => Column::Ease, + SortColumnProto::Lapses => Column::Lapses, + SortColumnProto::Interval => Column::Interval, + SortColumnProto::NoteCreation => Column::NoteCreation, + SortColumnProto::NoteMod => Column::NoteMod, + SortColumnProto::Notetype => Column::Notetype, + SortColumnProto::Reps => Column::Reps, + SortColumnProto::SortField => Column::SortField, + SortColumnProto::Tags => Column::Tags, } } } @@ -142,7 +136,7 @@ impl From> for SortMode { V::Custom(s) => SortMode::Custom(s), V::FromConfig(_) => SortMode::FromConfig, V::Builtin(b) => SortMode::Builtin { - kind: b.kind().into(), + column: b.column().into(), reverse: b.reverse, }, } diff --git a/rslib/src/browser_table.rs b/rslib/src/browser_table.rs index 45ee0e9f2..8e33daea0 100644 --- a/rslib/src/browser_table.rs +++ b/rslib/src/browser_table.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use itertools::Itertools; -use serde_repr::{Deserialize_repr, Serialize_repr}; +use serde::{Deserialize, Serialize}; use strum::{Display, EnumIter, EnumString}; use crate::error::{AnkiError, Result}; @@ -22,37 +22,47 @@ use crate::{ timestamp::{TimestampMillis, TimestampSecs}, }; -#[derive( - Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy, Display, EnumString, EnumIter, -)] +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy, Display, EnumIter, EnumString)] +#[serde(rename_all = "camelCase")] #[strum(serialize_all = "camelCase")] -#[repr(u8)] pub enum Column { + #[serde(rename = "")] #[strum(serialize = "")] Custom, Answer, CardMod, + #[serde(rename = "template")] #[strum(serialize = "template")] Cards, Deck, + #[serde(rename = "cardDue")] #[strum(serialize = "cardDue")] Due, + #[serde(rename = "cardEase")] #[strum(serialize = "cardEase")] Ease, + #[serde(rename = "cardLapses")] #[strum(serialize = "cardLapses")] Lapses, + #[serde(rename = "cardIvl")] #[strum(serialize = "cardIvl")] Interval, + #[serde(rename = "noteCrt")] #[strum(serialize = "noteCrt")] NoteCreation, NoteMod, + #[serde(rename = "note")] #[strum(serialize = "note")] Notetype, Question, + #[serde(rename = "cardReps")] #[strum(serialize = "cardReps")] Reps, + #[serde(rename = "noteFld")] #[strum(serialize = "noteFld")] SortField, + #[serde(rename = "noteTags")] + #[strum(serialize = "noteTags")] Tags, } diff --git a/rslib/src/config/mod.rs b/rslib/src/config/mod.rs index beabae235..e26239280 100644 --- a/rslib/src/config/mod.rs +++ b/rslib/src/config/mod.rs @@ -9,10 +9,9 @@ mod string; pub(crate) mod undo; pub use self::{bool::BoolKey, string::StringKey}; -use crate::browser_table; +use crate::browser_table::Column; use crate::prelude::*; use serde::{de::DeserializeOwned, Serialize}; -use serde_derive::Deserialize; use serde_repr::{Deserialize_repr, Serialize_repr}; use slog::warn; use strum::IntoStaticStr; @@ -48,9 +47,9 @@ pub(crate) enum ConfigKey { #[strum(to_string = "timeLim")] AnswerTimeLimitSecs, #[strum(to_string = "sortType")] - BrowserSortKind, + BrowserSortColumn, #[strum(to_string = "noteSortType")] - BrowserNoteSortKind, + BrowserNoteSortColumn, #[strum(to_string = "curDeck")] CurrentDeckId, #[strum(to_string = "curModel")] @@ -131,33 +130,29 @@ impl Collection { Ok(()) } - pub(crate) fn get_browser_sort_kind(&self) -> SortKind { - self.get_config_default(ConfigKey::BrowserSortKind) + pub(crate) fn get_browser_sort_column(&self) -> Column { + self.get_config_optional(ConfigKey::BrowserSortColumn) + .unwrap_or(Column::NoteCreation) } - pub(crate) fn get_browser_note_sort_kind(&self) -> SortKind { - self.get_config_default(ConfigKey::BrowserNoteSortKind) + pub(crate) fn get_browser_note_sort_column(&self) -> Column { + self.get_config_optional(ConfigKey::BrowserNoteSortColumn) + .unwrap_or(Column::NoteCreation) } - pub(crate) fn get_desktop_browser_card_columns(&self) -> Option> { + pub(crate) fn get_desktop_browser_card_columns(&self) -> Option> { self.get_config_optional(ConfigKey::DesktopBrowserCardColumns) } - pub(crate) fn set_desktop_browser_card_columns( - &mut self, - columns: Vec, - ) -> Result<()> { + pub(crate) fn set_desktop_browser_card_columns(&mut self, columns: Vec) -> Result<()> { self.set_config(ConfigKey::DesktopBrowserCardColumns, &columns) } - pub(crate) fn get_desktop_browser_note_columns(&self) -> Option> { + pub(crate) fn get_desktop_browser_note_columns(&self) -> Option> { self.get_config_optional(ConfigKey::DesktopBrowserNoteColumns) } - pub(crate) fn set_desktop_browser_note_columns( - &mut self, - columns: Vec, - ) -> Result<()> { + pub(crate) fn set_desktop_browser_note_columns(&mut self, columns: Vec) -> Result<()> { self.set_config(ConfigKey::DesktopBrowserNoteColumns, &columns) } @@ -269,47 +264,6 @@ impl Collection { } } -#[derive(Deserialize, PartialEq, Debug, Clone, Copy)] -#[serde(rename_all = "camelCase")] -pub enum SortKind { - NoteCards, - #[serde(rename = "noteCrt")] - NoteCreation, - NoteDue, - NoteEase, - #[serde(rename = "noteIvl")] - NoteInterval, - NoteLapses, - NoteMod, - #[serde(rename = "noteFld")] - SortField, - NoteReps, - #[serde(rename = "note")] - Notetype, - #[serde(rename = "noteTags")] - Tags, - CardMod, - #[serde(rename = "cardReps")] - Reps, - #[serde(rename = "cardDue")] - Due, - #[serde(rename = "cardEase")] - Ease, - #[serde(rename = "cardLapses")] - Lapses, - #[serde(rename = "cardIvl")] - Interval, - Deck, - #[serde(rename = "template")] - Cards, -} - -impl Default for SortKind { - fn default() -> Self { - Self::NoteCreation - } -} - // 2021 scheduler moves this into deck config pub(crate) enum NewReviewMix { Mix = 0, @@ -334,7 +288,7 @@ pub(crate) enum Weekday { #[cfg(test)] mod test { - use super::SortKind; + use super::*; use crate::collection::open_test_collection; use crate::decks::DeckId; @@ -342,7 +296,7 @@ mod test { fn defaults() { let col = open_test_collection(); assert_eq!(col.get_current_deck_id(), DeckId(1)); - assert_eq!(col.get_browser_sort_kind(), SortKind::SortField); + assert_eq!(col.get_browser_sort_column(), Column::SortField); } #[test] diff --git a/rslib/src/search/mod.rs b/rslib/src/search/mod.rs index 4a6e97358..96dc5564d 100644 --- a/rslib/src/search/mod.rs +++ b/rslib/src/search/mod.rs @@ -14,14 +14,8 @@ use rusqlite::types::FromSql; use std::borrow::Cow; use crate::{ - card::CardId, - card::CardType, - collection::Collection, - config::{BoolKey, SortKind}, - error::Result, - notes::NoteId, - prelude::AnkiError, - search::parser::parse, + browser_table::Column, card::CardId, card::CardType, collection::Collection, config::BoolKey, + error::Result, notes::NoteId, prelude::AnkiError, search::parser::parse, }; use sqlwriter::{RequiredTable, SqlWriter}; @@ -35,7 +29,7 @@ pub enum SearchItems { pub enum SortMode { NoOrder, FromConfig, - Builtin { kind: SortKind, reverse: bool }, + Builtin { column: Column, reverse: bool }, Custom(String), } @@ -69,7 +63,7 @@ impl SortMode { match self { SortMode::NoOrder => RequiredTable::CardsOrNotes, SortMode::FromConfig => unreachable!(), - SortMode::Builtin { kind, .. } => kind.required_table(), + SortMode::Builtin { column, .. } => column.required_table(), SortMode::Custom(ref text) => { if text.contains("n.") { if text.contains("c.") { @@ -85,28 +79,16 @@ impl SortMode { } } -impl SortKind { +impl Column { fn required_table(self) -> RequiredTable { match self { - SortKind::NoteCards - | SortKind::NoteCreation - | SortKind::NoteDue - | SortKind::NoteEase - | SortKind::SortField - | SortKind::NoteInterval - | SortKind::NoteLapses - | SortKind::NoteMod - | SortKind::NoteReps - | SortKind::Tags - | SortKind::Notetype => RequiredTable::Notes, - SortKind::Cards => RequiredTable::CardsAndNotes, - SortKind::CardMod - | SortKind::Reps - | SortKind::Due - | SortKind::Ease - | SortKind::Lapses - | SortKind::Interval - | SortKind::Deck => RequiredTable::Cards, + Column::Cards + | Column::NoteCreation + | Column::NoteMod + | Column::Notetype + | Column::SortField + | Column::Tags => RequiredTable::Notes, + _ => RequiredTable::CardsOrNotes, } } } @@ -144,10 +126,10 @@ impl Collection { match mode { SortMode::NoOrder => (), SortMode::FromConfig => unreachable!(), - SortMode::Builtin { kind, reverse } => { - prepare_sort(self, kind, items)?; + SortMode::Builtin { column, reverse } => { + prepare_sort(self, column, items)?; sql.push_str(" order by "); - write_order(sql, items, kind, reverse)?; + write_order(sql, items, column, reverse)?; } SortMode::Custom(order_clause) => { sql.push_str(" order by "); @@ -192,11 +174,11 @@ impl Collection { if mode == &SortMode::FromConfig { *mode = match items { SearchItems::Cards => SortMode::Builtin { - kind: self.get_browser_sort_kind(), + column: self.get_browser_sort_column(), reverse: self.get_bool(BoolKey::BrowserSortBackwards), }, SearchItems::Notes => SortMode::Builtin { - kind: self.get_browser_note_sort_kind(), + column: self.get_browser_note_sort_column(), reverse: self.get_bool(BoolKey::BrowserNoteSortBackwards), }, } @@ -205,15 +187,15 @@ impl Collection { } /// Add the order clause to the sql. -fn write_order(sql: &mut String, items: SearchItems, kind: SortKind, reverse: bool) -> Result<()> { +fn write_order(sql: &mut String, items: SearchItems, column: Column, reverse: bool) -> Result<()> { let order = match items { - SearchItems::Cards => card_order_from_sortkind(kind), - SearchItems::Notes => note_order_from_sortkind(kind), + SearchItems::Cards => card_order_from_sort_column(column), + SearchItems::Notes => note_order_from_sort_column(column), }; if order.is_empty() { return Err(AnkiError::invalid_input(format!( "Can't sort {:?} by {:?}.", - items, kind + items, column ))); } if reverse { @@ -229,70 +211,69 @@ fn write_order(sql: &mut String, items: SearchItems, kind: SortKind, reverse: bo Ok(()) } -fn card_order_from_sortkind(kind: SortKind) -> Cow<'static, str> { - match kind { - SortKind::NoteCreation => "n.id asc, c.ord asc".into(), - SortKind::NoteMod => "n.mod asc, c.ord asc".into(), - SortKind::SortField => "n.sfld collate nocase asc, c.ord asc".into(), - SortKind::CardMod => "c.mod asc".into(), - SortKind::Reps => "c.reps asc".into(), - SortKind::Due => "c.type asc, c.due asc".into(), - SortKind::Ease => format!("c.type = {} asc, c.factor asc", CardType::New as i8).into(), - SortKind::Lapses => "c.lapses asc".into(), - SortKind::Interval => "c.ivl asc".into(), - SortKind::Tags => "n.tags asc".into(), - SortKind::Deck => "(select pos from sort_order where did = c.did) asc".into(), - SortKind::Notetype => "(select pos from sort_order where ntid = n.mid) asc".into(), - SortKind::Cards => concat!( +fn card_order_from_sort_column(column: Column) -> Cow<'static, str> { + match column { + Column::CardMod => "c.mod asc".into(), + Column::Cards => concat!( "coalesce((select pos from sort_order where ntid = n.mid and ord = c.ord),", // need to fall back on ord 0 for cloze cards "(select pos from sort_order where ntid = n.mid and ord = 0)) asc" ) .into(), - _ => "".into(), + Column::Deck => "(select pos from sort_order where did = c.did) asc".into(), + Column::Due => "c.type asc, c.due asc".into(), + Column::Ease => format!("c.type = {} asc, c.factor asc", CardType::New as i8).into(), + Column::Interval => "c.ivl asc".into(), + Column::Lapses => "c.lapses asc".into(), + Column::NoteCreation => "n.id asc, c.ord asc".into(), + Column::NoteMod => "n.mod asc, c.ord asc".into(), + Column::Notetype => "(select pos from sort_order where ntid = n.mid) asc".into(), + Column::Reps => "c.reps asc".into(), + Column::SortField => "n.sfld collate nocase asc, c.ord asc".into(), + Column::Tags => "n.tags asc".into(), + Column::Answer | Column::Custom | Column::Question => "".into(), } } -fn note_order_from_sortkind(kind: SortKind) -> Cow<'static, str> { - match kind { - SortKind::Deck - | SortKind::CardMod - | SortKind::NoteCards - | SortKind::NoteDue - | SortKind::NoteEase - | SortKind::NoteInterval - | SortKind::NoteLapses - | SortKind::NoteReps => "(select pos from sort_order where nid = n.id) asc".into(), - SortKind::NoteCreation => "n.id asc".into(), - SortKind::SortField => "n.sfld collate nocase asc".into(), - SortKind::NoteMod => "n.mod asc".into(), - SortKind::Tags => "n.tags asc".into(), - SortKind::Notetype => "(select pos from sort_order where ntid = n.mid) asc".into(), - _ => "".into(), +fn note_order_from_sort_column(column: Column) -> Cow<'static, str> { + match column { + Column::CardMod + | Column::Cards + | Column::Deck + | Column::Due + | Column::Ease + | Column::Interval + | Column::Lapses + | Column::Reps => "(select pos from sort_order where nid = n.id) asc".into(), + Column::NoteCreation => "n.id asc".into(), + Column::NoteMod => "n.mod asc".into(), + Column::Notetype => "(select pos from sort_order where ntid = n.mid) asc".into(), + Column::SortField => "n.sfld collate nocase asc".into(), + Column::Tags => "n.tags asc".into(), + Column::Answer | Column::Custom | Column::Question => "".into(), } } -fn prepare_sort(col: &mut Collection, kind: SortKind, items: SearchItems) -> Result<()> { - use SortKind::*; - let notes_mode = items == SearchItems::Notes; - let sql = match kind { - Deck => { - if notes_mode { - include_str!("note_decks_order.sql") - } else { - include_str!("deck_order.sql") - } - } - CardMod if notes_mode => include_str!("card_mod_order.sql"), - Cards => include_str!("template_order.sql"), - NoteCards => include_str!("note_cards_order.sql"), - NoteDue => include_str!("note_due_order.sql"), - NoteEase => include_str!("note_ease_order.sql"), - NoteInterval => include_str!("note_interval_order.sql"), - NoteLapses => include_str!("note_lapses_order.sql"), - NoteReps => include_str!("note_reps_order.sql"), - Notetype => include_str!("notetype_order.sql"), - _ => return Ok(()), +fn prepare_sort(col: &mut Collection, column: Column, items: SearchItems) -> Result<()> { + let sql = match items { + SearchItems::Cards => match column { + Column::Cards => include_str!("template_order.sql"), + Column::Deck => include_str!("deck_order.sql"), + Column::Notetype => include_str!("notetype_order.sql"), + _ => return Ok(()), + }, + SearchItems::Notes => match column { + Column::Cards => include_str!("note_cards_order.sql"), + Column::CardMod => include_str!("card_mod_order.sql"), + Column::Deck => include_str!("note_decks_order.sql"), + Column::Due => include_str!("note_due_order.sql"), + Column::Ease => include_str!("note_ease_order.sql"), + Column::Interval => include_str!("note_interval_order.sql"), + Column::Lapses => include_str!("note_lapses_order.sql"), + Column::Reps => include_str!("note_reps_order.sql"), + Column::Notetype => include_str!("notetype_order.sql"), + _ => return Ok(()), + }, }; col.storage.db.execute_batch(sql)?;