diff --git a/Cargo.lock b/Cargo.lock index 6d6651800..32f15ab2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2099,7 +2099,7 @@ dependencies = [ [[package]] name = "fsrs" version = "3.0.0" -source = "git+https://github.com/open-spaced-repetition/fsrs-rs.git?rev=22f8e453c120f5bc5996f86558a559c6b7abfc49#22f8e453c120f5bc5996f86558a559c6b7abfc49" +source = "git+https://github.com/open-spaced-repetition/fsrs-rs.git?rev=08d90d1363b0c4722422bf0ef71ed8fd7d053f8a#08d90d1363b0c4722422bf0ef71ed8fd7d053f8a" dependencies = [ "burn", "itertools 0.12.1", diff --git a/Cargo.toml b/Cargo.toml index e3299a740..c16294236 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ rev = "184b2ca50ed39ca43da13f0b830a463861adb9ca" [workspace.dependencies.fsrs] # version = "=2.0.3" git = "https://github.com/open-spaced-repetition/fsrs-rs.git" -rev = "22f8e453c120f5bc5996f86558a559c6b7abfc49" +rev = "08d90d1363b0c4722422bf0ef71ed8fd7d053f8a" # path = "../open-spaced-repetition/fsrs-rs" [workspace.dependencies] diff --git a/cargo/format/rust-toolchain.toml b/cargo/format/rust-toolchain.toml index 66a834c36..42af1fe66 100644 --- a/cargo/format/rust-toolchain.toml +++ b/cargo/format/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2023-09-02" +channel = "nightly-2025-03-20" profile = "minimal" components = ["rustfmt"] diff --git a/ftl/qt/about.ftl b/ftl/qt/about.ftl index 6fd190638..b841e0843 100644 --- a/ftl/qt/about.ftl +++ b/ftl/qt/about.ftl @@ -8,3 +8,5 @@ about-if-you-have-contributed-and-are = If you have contributed and are not on t about-version = Version { $val } about-visit-website = Visit website about-written-by-damien-elmes-with-patches = Written by Damien Elmes, with patches, translation, testing and design from:
{ $cont } +# appended to the end of the contributor list in the about screen +about-and-others = and others diff --git a/pylib/anki/utils.py b/pylib/anki/utils.py index b5382e6df..1b4212620 100644 --- a/pylib/anki/utils.py +++ b/pylib/anki/utils.py @@ -248,6 +248,10 @@ is_mac = sys.platform.startswith("darwin") is_win = sys.platform.startswith("win32") # also covers *BSD is_lin = not is_mac and not is_win +is_gnome = ( + "gnome" in os.getenv("XDG_CURRENT_DESKTOP", "").lower() + or "gnome" in os.getenv("DESKTOP_SESSION", "").lower() +) dev_mode = os.getenv("ANKIDEV", "") hmr_mode = os.getenv("HMR", "") diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index af3797049..fb9222521 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -59,7 +59,7 @@ from anki._backend import RustBackend from anki.buildinfo import version as _version from anki.collection import Collection from anki.consts import HELP_SITE -from anki.utils import checksum, is_lin, is_mac +from anki.utils import checksum, is_gnome, is_lin, is_mac from aqt import gui_hooks from aqt.log import setup_logging from aqt.qt import * @@ -614,7 +614,7 @@ def _run(argv: list[str] | None = None, exec: bool = True) -> AnkiApp | None: ) wayland_forced = os.getenv("ANKI_WAYLAND") - if packaged and wayland_configured: + if (packaged or is_gnome) and wayland_configured: if wayland_forced or not x11_available: # Work around broken fractional scaling in Wayland # https://bugreports.qt.io/browse/QTBUG-113574 diff --git a/qt/aqt/about.py b/qt/aqt/about.py index 659e380ea..d0d7157af 100644 --- a/qt/aqt/about.py +++ b/qt/aqt/about.py @@ -222,7 +222,7 @@ def show(mw: aqt.AnkiQt) -> QDialog: ) abouttext += "
" + tr.about_written_by_damien_elmes_with_patches( - cont=", ".join(allusers) + cont=", ".join(allusers) + f", {tr.about_and_others()}" ) abouttext += f"
{tr.about_if_you_have_contributed_and_are()}" abouttext += f"
{tr.about_a_big_thanks_to_all_the()}"
diff --git a/rslib/src/import_export/text/csv/metadata.rs b/rslib/src/import_export/text/csv/metadata.rs
index a69acfe9e..7e2f64f5e 100644
--- a/rslib/src/import_export/text/csv/metadata.rs
+++ b/rslib/src/import_export/text/csv/metadata.rs
@@ -121,19 +121,34 @@ impl Collection {
}
fn parse_meta_value(&mut self, key: &str, value: &str, metadata: &mut CsvMetadata) {
+ // trim potential delimiters past the first char* if
+ // metadata line was mistakenly exported as a record
+ // *to allow cases like #separator:,
+ // ASSUMPTION: delimiters are not ascii-alphanumeric
+ let trimmed_value = value
+ .char_indices()
+ .nth(1)
+ .and_then(|(i, _)| {
+ value[i..] // SAFETY: char_indices are on char boundaries
+ .find(|c| !char::is_ascii_alphanumeric(&c))
+ .map(|j| value.split_at(i + j).0)
+ })
+ .unwrap_or(value);
+
match key.trim().to_ascii_lowercase().as_str() {
"separator" => {
- if let Some(delimiter) = delimiter_from_value(value) {
+ if let Some(delimiter) = delimiter_from_value(trimmed_value) {
metadata.delimiter = delimiter as i32;
metadata.force_delimiter = true;
}
}
"html" => {
- if let Ok(is_html) = value.to_lowercase().parse() {
+ if let Ok(is_html) = trimmed_value.to_lowercase().parse() {
metadata.is_html = is_html;
metadata.force_is_html = true;
}
}
+ // freeform values cannot be trimmed thus without knowing the exact delimiter
"tags" => metadata.global_tags = collect_tags(value),
"columns" => {
if let Ok(columns) = parse_columns(value, metadata.delimiter()) {
@@ -151,22 +166,22 @@ impl Collection {
}
}
"notetype column" => {
- if let Ok(n) = value.trim().parse() {
+ if let Ok(n) = trimmed_value.trim().parse() {
metadata.notetype = Some(CsvNotetype::NotetypeColumn(n));
}
}
"deck column" => {
- if let Ok(n) = value.trim().parse() {
+ if let Ok(n) = trimmed_value.trim().parse() {
metadata.deck = Some(CsvDeck::DeckColumn(n));
}
}
"tags column" => {
- if let Ok(n) = value.trim().parse() {
+ if let Ok(n) = trimmed_value.trim().parse() {
metadata.tags_column = n;
}
}
"guid column" => {
- if let Ok(n) = value.trim().parse() {
+ if let Ok(n) = trimmed_value.trim().parse() {
metadata.guid_column = n;
}
}
@@ -891,4 +906,32 @@ pub(in crate::import_export) mod test {
maybe_set_tags_column(&mut metadata, &meta_columns);
assert_eq!(metadata.tags_column, 4);
}
+
+ #[test]
+ fn should_allow_non_freeform_metadata_lines_to_be_suffixed_by_delimiters() {
+ let mut col = Collection::new();
+ let metadata = metadata!(
+ col,
+ r#"
+#separator:Pipe,,,,,,,
+#html:true|||||
+#tags:foo bar::世界,,,
+#guid column:8
+#tags column:123abc
+ "#
+ .trim()
+ );
+ assert_eq!(metadata.delimiter(), Delimiter::Pipe);
+ assert!(metadata.is_html);
+ assert_eq!(metadata.guid_column, 8);
+ // tags is freeform, potential delimiters aren't trimmed
+ assert_eq!(metadata.global_tags, ["foo", "bar::世界,,,"]);
+ // ascii alphanumerics aren't trimmed away
+ assert_eq!(metadata.tags_column, 0);
+
+ assert_eq!(
+ metadata!(col, "#separator:\t|,:\n").delimiter(),
+ Delimiter::Tab
+ );
+ }
}
diff --git a/rslib/src/scheduler/fsrs/memory_state.rs b/rslib/src/scheduler/fsrs/memory_state.rs
index 5efef07a6..90920f4bb 100644
--- a/rslib/src/scheduler/fsrs/memory_state.rs
+++ b/rslib/src/scheduler/fsrs/memory_state.rs
@@ -242,6 +242,7 @@ pub(crate) struct FsrsItemForMemoryState {
/// When revlogs have been truncated, this stores the initial state at first
/// review
pub starting_state: Option