From aeedb4dc110f85cb6d37db15e6b84f9abf3f374c Mon Sep 17 00:00:00 2001 From: RumovZ Date: Sat, 12 Jun 2021 10:02:21 +0200 Subject: [PATCH 1/9] Add check for out-of-place/missing clozes --- pylib/anki/notes.py | 12 +++--- rslib/backend.proto | 7 +++- rslib/src/backend/notes.rs | 6 +-- rslib/src/cloze.rs | 4 ++ rslib/src/notes/mod.rs | 79 ++++++++++++++++++++++++++++---------- rslib/src/notetype/mod.rs | 16 ++++++++ 6 files changed, 91 insertions(+), 33 deletions(-) diff --git a/pylib/anki/notes.py b/pylib/anki/notes.py index 2741bbc47..176002a3c 100644 --- a/pylib/anki/notes.py +++ b/pylib/anki/notes.py @@ -14,7 +14,7 @@ from anki.consts import MODEL_STD from anki.models import NotetypeDict, NotetypeId, TemplateDict from anki.utils import joinFields -DuplicateOrEmptyResult = _pb.NoteIsDuplicateOrEmptyOut.State +NoteFieldsCheckResult = DuplicateOrEmptyResult = _pb.NoteFieldsCheckOut.State # types NoteId = NewType("NoteId", int) @@ -190,12 +190,10 @@ class Note: addTag = add_tag delTag = remove_tag - # Unique/duplicate check + # Unique/duplicate/cloze check ################################################## - def duplicate_or_empty(self) -> DuplicateOrEmptyResult.V: - return self.col._backend.note_is_duplicate_or_empty( - self._to_backend_note() - ).state + def fields_check(self) -> NoteFieldsCheckResult.V: + return self.col._backend.note_fields_check(self._to_backend_note()).state - dupeOrEmpty = duplicate_or_empty + dupeOrEmpty = duplicate_or_empty = fields_check diff --git a/rslib/backend.proto b/rslib/backend.proto index 0ebfb68f3..521432dfe 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -174,7 +174,7 @@ service NotesService { rpc ClozeNumbersInNote(Note) returns (ClozeNumbersInNoteOut); rpc AfterNoteUpdates(AfterNoteUpdatesIn) returns (OpChangesWithCount); rpc FieldNamesForNotes(FieldNamesForNotesIn) returns (FieldNamesForNotesOut); - rpc NoteIsDuplicateOrEmpty(Note) returns (NoteIsDuplicateOrEmptyOut); + rpc NoteFieldsCheck(Note) returns (NoteFieldsCheckOut); rpc CardsOfNote(NoteId) returns (CardIds); } @@ -1231,11 +1231,14 @@ message ReparentDecksIn { int64 new_parent = 2; } -message NoteIsDuplicateOrEmptyOut { +message NoteFieldsCheckOut { enum State { NORMAL = 0; EMPTY = 1; DUPLICATE = 2; + MISSING_CLOZE = 3; + NOTETYPE_NOT_CLOZE = 4; + FIELD_NOT_CLOZE = 5; } State state = 1; } diff --git a/rslib/src/backend/notes.rs b/rslib/src/backend/notes.rs index 673c32058..59025c1f8 100644 --- a/rslib/src/backend/notes.rs +++ b/rslib/src/backend/notes.rs @@ -120,11 +120,11 @@ impl NotesService for Backend { }) } - fn note_is_duplicate_or_empty(&self, input: pb::Note) -> Result { + fn note_fields_check(&self, input: pb::Note) -> Result { let note: Note = input.into(); self.with_col(|col| { - col.note_is_duplicate_or_empty(¬e) - .map(|r| pb::NoteIsDuplicateOrEmptyOut { state: r as i32 }) + col.note_fields_check(¬e) + .map(|r| pb::NoteFieldsCheckOut { state: r as i32 }) }) } diff --git a/rslib/src/cloze.rs b/rslib/src/cloze.rs index 01f219ed3..dcd064c2f 100644 --- a/rslib/src/cloze.rs +++ b/rslib/src/cloze.rs @@ -139,6 +139,10 @@ pub fn expand_clozes_to_reveal_latex(text: &str) -> String { buf } +pub(crate) fn contains_cloze(text: &str) -> bool { + CLOZE.is_match(text) +} + pub fn cloze_numbers_in_string(html: &str) -> HashSet { let mut set = HashSet::with_capacity(4); add_cloze_numbers_in_string(html, &mut set); diff --git a/rslib/src/notes/mod.rs b/rslib/src/notes/mod.rs index 8ce7ef894..d1ddffbf6 100644 --- a/rslib/src/notes/mod.rs +++ b/rslib/src/notes/mod.rs @@ -14,7 +14,8 @@ use num_integer::Integer; use crate::{ backend_proto as pb, - backend_proto::note_is_duplicate_or_empty_out::State as DuplicateState, + backend_proto::note_fields_check_out::State as NoteFieldsState, + cloze::contains_cloze, decks::DeckId, define_newtype, error::{AnkiError, Result}, @@ -529,36 +530,72 @@ impl Collection { Ok(changed_notes) } - pub(crate) fn note_is_duplicate_or_empty(&self, note: &Note) -> Result { - if let Some(field1) = note.fields.get(0) { + /// Check if the note's first field is empty or a duplicate. Then for cloze + /// notetypes, check if there is a cloze in a non-cloze field or if there's + /// no cloze at all. For other notetypes, just check if there's a cloze. + pub(crate) fn note_fields_check(&mut self, note: &Note) -> Result { + if let Some(text) = note.fields.get(0) { let field1 = if self.get_config_bool(BoolKey::NormalizeNoteText) { - normalize_to_nfc(field1) + normalize_to_nfc(text) } else { - field1.into() + text.into() }; let stripped = strip_html_preserving_media_filenames(&field1); if stripped.trim().is_empty() { - Ok(DuplicateState::Empty) + Ok(NoteFieldsState::Empty) + } else if self.is_duplicate(&stripped, note)? { + Ok(NoteFieldsState::Duplicate) } else { - let csum = field_checksum(&stripped); - let have_dupe = self - .storage - .note_fields_by_checksum(note.notetype_id, csum)? - .into_iter() - .any(|(nid, field)| { - nid != note.id && strip_html_preserving_media_filenames(&field) == stripped - }); - - if have_dupe { - Ok(DuplicateState::Duplicate) - } else { - Ok(DuplicateState::Normal) - } + self.field_cloze_check(note) } } else { - Ok(DuplicateState::Empty) + Ok(NoteFieldsState::Empty) } } + + fn is_duplicate(&self, first_field: &str, note: &Note) -> Result { + let csum = field_checksum(&first_field); + Ok(self + .storage + .note_fields_by_checksum(note.notetype_id, csum)? + .into_iter() + .any(|(nid, field)| { + nid != note.id && strip_html_preserving_media_filenames(&field) == first_field + })) + } + + fn field_cloze_check(&mut self, note: &Note) -> Result { + let notetype = self + .get_notetype(note.notetype_id)? + .ok_or(AnkiError::NotFound)?; + let cloze_fields = notetype.cloze_fields(); + let mut has_cloze = false; + let extraneous_cloze = note.fields.iter().enumerate().find_map(|(i, field)| { + if notetype.is_cloze() { + if contains_cloze(field) { + if cloze_fields.contains(&i) { + has_cloze = true; + None + } else { + Some(NoteFieldsState::FieldNotCloze) + } + } else { + None + } + } else if contains_cloze(field) { + Some(NoteFieldsState::NotetypeNotCloze) + } else { + None + } + }); + Ok(if let Some(state) = extraneous_cloze { + state + } else if notetype.is_cloze() && !has_cloze { + NoteFieldsState::MissingCloze + } else { + NoteFieldsState::Normal + }) + } } /// The existing note pulled from the DB will have sfld and csum set, but the diff --git a/rslib/src/notetype/mod.rs b/rslib/src/notetype/mod.rs index 13aa16cc6..ab5590a12 100644 --- a/rslib/src/notetype/mod.rs +++ b/rslib/src/notetype/mod.rs @@ -548,6 +548,22 @@ impl Notetype { pub(crate) fn is_cloze(&self) -> bool { matches!(self.config.kind(), NotetypeKind::Cloze) } + + /// Return all clozable fields. A field is clozable when it belongs to a cloze + /// notetype and a 'cloze' filter is applied to it in the template. + pub(crate) fn cloze_fields(&self) -> HashSet { + if !self.is_cloze() { + HashSet::new() + } else if let Some((Some(front), _)) = self.parsed_templates().get(0) { + front + .cloze_fields() + .iter() + .filter_map(|name| self.get_field_ord(name)) + .collect() + } else { + HashSet::new() + } + } } /// True if the slice is empty or either template of the first tuple doesn't have a cloze field. From 9acc03773a8a8ed76476a9de7fcc32eedac0715d Mon Sep 17 00:00:00 2001 From: RumovZ Date: Sat, 12 Jun 2021 10:05:42 +0200 Subject: [PATCH 2/9] Check for out-of-place/missing clozes when adding --- ftl/core/adding.ftl | 4 +++- pylib/.pylintrc | 2 +- qt/aqt/addcards.py | 19 +++++++++---------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ftl/core/adding.ftl b/ftl/core/adding.ftl index 03ea6b865..934b014fb 100644 --- a/ftl/core/adding.ftl +++ b/ftl/core/adding.ftl @@ -6,4 +6,6 @@ adding-history = History adding-note-deleted = (Note deleted) adding-shortcut = Shortcut: { $val } adding-the-first-field-is-empty = The first field is empty. -adding-you-have-a-cloze-deletion-note = You have a cloze deletion note type but have not made any cloze deletions. Proceed? +adding-missing-cloze = You have a cloze notetype but have not made any cloze deletions. +adding-cloze-outside-cloze-notetype = Cloze deletion can only be used on cloze notetypes. +adding-cloze-outside-cloze-field = Cloze deletion can only be used in fields which use the 'cloze:' filter (see 'Cards...'). diff --git a/pylib/.pylintrc b/pylib/.pylintrc index c12747171..ba446e936 100644 --- a/pylib/.pylintrc +++ b/pylib/.pylintrc @@ -11,7 +11,7 @@ ignored-classes= QueuedCards, UnburyDeckIn, BuryOrSuspendCardsIn, - NoteIsDuplicateOrEmptyOut, + NoteFieldsCheckOut, BackendError, SetDeckCollapsedIn, diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py index 9fe86773a..0574fdd70 100644 --- a/qt/aqt/addcards.py +++ b/qt/aqt/addcards.py @@ -6,10 +6,9 @@ from typing import Callable, List, Optional import aqt.editor import aqt.forms from anki.collection import OpChanges, SearchNode -from anki.consts import MODEL_CLOZE from anki.decks import DeckId from anki.models import NotetypeId -from anki.notes import DuplicateOrEmptyResult, Note, NoteId +from anki.notes import Note, NoteFieldsCheckResult, NoteId from anki.utils import htmlToTextLine, isMac from aqt import AnkiQt, gui_hooks from aqt.deckchooser import DeckChooser @@ -231,9 +230,15 @@ class AddCards(QDialog): ).run_in_background() def _note_can_be_added(self, note: Note) -> bool: - result = note.duplicate_or_empty() - if result == DuplicateOrEmptyResult.EMPTY: + result = note.fields_check() + if result == NoteFieldsCheckResult.EMPTY: problem = tr.adding_the_first_field_is_empty() + elif result == NoteFieldsCheckResult.MISSING_CLOZE: + problem = tr.adding_missing_cloze() + elif result == NoteFieldsCheckResult.NOTETYPE_NOT_CLOZE: + problem = tr.adding_cloze_outside_cloze_notetype() + elif result == NoteFieldsCheckResult.FIELD_NOT_CLOZE: + problem = tr.adding_cloze_outside_cloze_field() else: # duplicate entries are allowed these days problem = None @@ -244,12 +249,6 @@ class AddCards(QDialog): showWarning(problem, help=HelpPage.ADDING_CARD_AND_NOTE) return False - # missing cloze deletion? - if note.model()["type"] == MODEL_CLOZE: - if not note.cloze_numbers_in_fields(): - if not askUser(tr.adding_you_have_a_cloze_deletion_note()): - return False - return True def keyPressEvent(self, evt: QKeyEvent) -> None: From ab7c07e830a483a7e173b22bdbeae3adc14757a0 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Sat, 12 Jun 2021 17:34:46 +0200 Subject: [PATCH 3/9] Use fields_check() instead of duplicate_or_empty() --- pylib/tests/test_collection.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pylib/tests/test_collection.py b/pylib/tests/test_collection.py index 95b941708..f92daa365 100644 --- a/pylib/tests/test_collection.py +++ b/pylib/tests/test_collection.py @@ -74,15 +74,15 @@ def test_noteAddDelete(): c0 = note.cards()[0] assert "three" in c0.q() # it should not be a duplicate - assert not note.duplicate_or_empty() + assert not note.fields_check() # now let's make a duplicate note2 = col.newNote() note2["Front"] = "one" note2["Back"] = "" - assert note2.duplicate_or_empty() + assert note2.fields_check() # empty first field should not be permitted either note2["Front"] = " " - assert note2.duplicate_or_empty() + assert note2.fields_check() def test_fieldChecksum(): From d8c3e7110585c4975162c548524893022d94bbe3 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Sat, 12 Jun 2021 17:35:40 +0200 Subject: [PATCH 4/9] Add editor hints for cloze errors --- qt/aqt/editor.py | 30 +++++++++++++++++++++--------- ts/editor/fields.scss | 4 +++- ts/editor/index.ts | 9 +++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index 5caf8e546..f9f60a7af 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -27,7 +27,7 @@ from anki.collection import Config, SearchNode from anki.consts import MODEL_CLOZE from anki.hooks import runFilter from anki.httpclient import HttpClient -from anki.notes import DuplicateOrEmptyResult, Note +from anki.notes import Note, NoteFieldsCheckResult from anki.utils import checksum, isLin, isWin, namedtmp from aqt import AnkiQt, colors, gui_hooks from aqt.operations import QueryOp @@ -79,8 +79,10 @@ audio = ( _html = """
- %s + {}
+
{}
+
{}
""" @@ -129,7 +131,11 @@ class Editor: # then load page self.web.stdHtml( - _html % tr.editing_show_duplicates(), + _html.format( + tr.editing_show_duplicates(), + tr.adding_cloze_outside_cloze_notetype(), + tr.adding_cloze_outside_cloze_field(), + ), css=[ "css/editor.css", ], @@ -437,7 +443,7 @@ $editorToolbar.then(({{ toolbar }}) => toolbar.appendGroup({{ self.widget.show() self.updateTags() - dupe_status = self.note.duplicate_or_empty() + note_fields_status = self.note.fields_check() def oncallback(arg: Any) -> None: if not self.note: @@ -445,7 +451,7 @@ $editorToolbar.then(({{ toolbar }}) => toolbar.appendGroup({{ self.setupForegroundButton() # we currently do this synchronously to ensure we load before the # sidebar on browser startup - self._update_duplicate_display(dupe_status) + self._update_duplicate_display(note_fields_status) if focusTo is not None: self.web.setFocus() gui_hooks.editor_did_load_note(self) @@ -494,25 +500,31 @@ $editorToolbar.then(({{ toolbar }}) => toolbar.appendGroup({{ if not note: return - def on_done(result: DuplicateOrEmptyResult.V) -> None: + def on_done(result: NoteFieldsCheckResult.V) -> None: if self.note != note: return self._update_duplicate_display(result) QueryOp( parent=self.parentWindow, - op=lambda _: note.duplicate_or_empty(), + op=lambda _: note.fields_check(), success=on_done, ).run_in_background() checkValid = _check_and_update_duplicate_display_async - def _update_duplicate_display(self, result: DuplicateOrEmptyResult.V) -> None: + def _update_duplicate_display(self, result: NoteFieldsCheckResult.V) -> None: cols = [""] * len(self.note.fields) - if result == DuplicateOrEmptyResult.DUPLICATE: + wrong_notetype = wrong_field = "false" + if result == NoteFieldsCheckResult.DUPLICATE: cols[0] = "dupe" + elif result == NoteFieldsCheckResult.NOTETYPE_NOT_CLOZE: + wrong_notetype = "true" + elif result == NoteFieldsCheckResult.FIELD_NOT_CLOZE: + wrong_field = "true" self.web.eval(f"setBackgrounds({json.dumps(cols)});") + self.web.eval(f"setClozeHints({wrong_notetype}, {wrong_field});") def showDupes(self) -> None: aqt.dialogs.open( diff --git a/ts/editor/fields.scss b/ts/editor/fields.scss index a84047681..f5533bf65 100644 --- a/ts/editor/fields.scss +++ b/ts/editor/fields.scss @@ -28,7 +28,9 @@ padding: 0; } -#dupes { +#dupes, +#not-a-cloze-notetype, +#not-a-cloze-field { position: sticky; bottom: 0; diff --git a/ts/editor/index.ts b/ts/editor/index.ts index eb2980e2c..a118203f6 100644 --- a/ts/editor/index.ts +++ b/ts/editor/index.ts @@ -144,6 +144,15 @@ export function setBackgrounds(cols: ("dupe" | "")[]): void { .classList.toggle("is-inactive", !cols.includes("dupe")); } +export function setClozeHints(wrong_notetype: boolean, wrong_field: boolean): void { + document + .getElementById("not-a-cloze-notetype")! + .classList.toggle("is-inactive", !wrong_notetype); + document + .getElementById("not-a-cloze-field")! + .classList.toggle("is-inactive", !wrong_field); +} + export function setFonts(fonts: [string, number, boolean][]): void { forEditorField( fonts, From dcfb2f10520e8bde9741fcb44bc2465261c69d4d Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 14 Jun 2021 10:45:40 +1000 Subject: [PATCH 5/9] multiple assignment confuses mypy --- pylib/anki/notes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylib/anki/notes.py b/pylib/anki/notes.py index 176002a3c..2f5e3f1f4 100644 --- a/pylib/anki/notes.py +++ b/pylib/anki/notes.py @@ -14,7 +14,8 @@ from anki.consts import MODEL_STD from anki.models import NotetypeDict, NotetypeId, TemplateDict from anki.utils import joinFields -NoteFieldsCheckResult = DuplicateOrEmptyResult = _pb.NoteFieldsCheckOut.State +DuplicateOrEmptyResult = _pb.NoteFieldsCheckOut.State +NoteFieldsCheckResult = _pb.NoteFieldsCheckOut.State # types NoteId = NewType("NoteId", int) From 38b8e74f71648d16e174535b6a7e165b12debd8a Mon Sep 17 00:00:00 2001 From: RumovZ Date: Mon, 14 Jun 2021 09:49:44 +0200 Subject: [PATCH 6/9] Remove desktop-specific 'Cards...' hint --- ftl/core/adding.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftl/core/adding.ftl b/ftl/core/adding.ftl index 934b014fb..035717547 100644 --- a/ftl/core/adding.ftl +++ b/ftl/core/adding.ftl @@ -8,4 +8,4 @@ adding-shortcut = Shortcut: { $val } adding-the-first-field-is-empty = The first field is empty. adding-missing-cloze = You have a cloze notetype but have not made any cloze deletions. adding-cloze-outside-cloze-notetype = Cloze deletion can only be used on cloze notetypes. -adding-cloze-outside-cloze-field = Cloze deletion can only be used in fields which use the 'cloze:' filter (see 'Cards...'). +adding-cloze-outside-cloze-field = Cloze deletion can only be used in fields which use the 'cloze:' filter. This is typically the first field. From 5574cb1cfe74e33d4dc6dea17ca811c3cbbd39d0 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Mon, 14 Jun 2021 10:20:53 +0200 Subject: [PATCH 7/9] Make missing cloze a warning again --- ftl/core/adding.ftl | 2 +- qt/aqt/addcards.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ftl/core/adding.ftl b/ftl/core/adding.ftl index 035717547..4a9756dc5 100644 --- a/ftl/core/adding.ftl +++ b/ftl/core/adding.ftl @@ -6,6 +6,6 @@ adding-history = History adding-note-deleted = (Note deleted) adding-shortcut = Shortcut: { $val } adding-the-first-field-is-empty = The first field is empty. -adding-missing-cloze = You have a cloze notetype but have not made any cloze deletions. +adding-you-have-a-cloze-deletion-note = You have a cloze notetype but have not made any cloze deletions. Proceed? adding-cloze-outside-cloze-notetype = Cloze deletion can only be used on cloze notetypes. adding-cloze-outside-cloze-field = Cloze deletion can only be used in fields which use the 'cloze:' filter. This is typically the first field. diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py index 0574fdd70..7a2f15154 100644 --- a/qt/aqt/addcards.py +++ b/qt/aqt/addcards.py @@ -234,7 +234,8 @@ class AddCards(QDialog): if result == NoteFieldsCheckResult.EMPTY: problem = tr.adding_the_first_field_is_empty() elif result == NoteFieldsCheckResult.MISSING_CLOZE: - problem = tr.adding_missing_cloze() + if not askUser(tr.adding_you_have_a_cloze_deletion_note()): + return False elif result == NoteFieldsCheckResult.NOTETYPE_NOT_CLOZE: problem = tr.adding_cloze_outside_cloze_notetype() elif result == NoteFieldsCheckResult.FIELD_NOT_CLOZE: From 0f549d2b82f3916f853cec3a89d4f1fe9ba60601 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Mon, 14 Jun 2021 10:21:42 +0200 Subject: [PATCH 8/9] Merge cloze hint divs into one --- qt/aqt/editor.py | 17 ++++++----------- ts/editor/fields.scss | 3 +-- ts/editor/index.ts | 9 ++------- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index f9f60a7af..b07137d69 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -81,8 +81,7 @@ _html = """
{}
-
{}
-
{}
+
""" @@ -131,11 +130,7 @@ class Editor: # then load page self.web.stdHtml( - _html.format( - tr.editing_show_duplicates(), - tr.adding_cloze_outside_cloze_notetype(), - tr.adding_cloze_outside_cloze_field(), - ), + _html.format(tr.editing_show_duplicates()), css=[ "css/editor.css", ], @@ -515,16 +510,16 @@ $editorToolbar.then(({{ toolbar }}) => toolbar.appendGroup({{ def _update_duplicate_display(self, result: NoteFieldsCheckResult.V) -> None: cols = [""] * len(self.note.fields) - wrong_notetype = wrong_field = "false" + cloze_hint = "" if result == NoteFieldsCheckResult.DUPLICATE: cols[0] = "dupe" elif result == NoteFieldsCheckResult.NOTETYPE_NOT_CLOZE: - wrong_notetype = "true" + cloze_hint = tr.adding_cloze_outside_cloze_notetype() elif result == NoteFieldsCheckResult.FIELD_NOT_CLOZE: - wrong_field = "true" + cloze_hint = tr.adding_cloze_outside_cloze_field() self.web.eval(f"setBackgrounds({json.dumps(cols)});") - self.web.eval(f"setClozeHints({wrong_notetype}, {wrong_field});") + self.web.eval(f"setClozeHint({json.dumps(cloze_hint)});") def showDupes(self) -> None: aqt.dialogs.open( diff --git a/ts/editor/fields.scss b/ts/editor/fields.scss index f5533bf65..857a16ac1 100644 --- a/ts/editor/fields.scss +++ b/ts/editor/fields.scss @@ -29,8 +29,7 @@ } #dupes, -#not-a-cloze-notetype, -#not-a-cloze-field { +#cloze-hint { position: sticky; bottom: 0; diff --git a/ts/editor/index.ts b/ts/editor/index.ts index a118203f6..6056047cc 100644 --- a/ts/editor/index.ts +++ b/ts/editor/index.ts @@ -144,13 +144,8 @@ export function setBackgrounds(cols: ("dupe" | "")[]): void { .classList.toggle("is-inactive", !cols.includes("dupe")); } -export function setClozeHints(wrong_notetype: boolean, wrong_field: boolean): void { - document - .getElementById("not-a-cloze-notetype")! - .classList.toggle("is-inactive", !wrong_notetype); - document - .getElementById("not-a-cloze-field")! - .classList.toggle("is-inactive", !wrong_field); +export function setClozeHint(cloze_hint: string): void { + document.getElementById("cloze-hint")!.innerHTML = cloze_hint; } export function setFonts(fonts: [string, number, boolean][]): void { From 87a50f22e636aef618bf9aa4e3593c42ba068f61 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Wed, 16 Jun 2021 11:42:40 +0200 Subject: [PATCH 9/9] Assign dupe error the lowest precedence --- rslib/src/notes/mod.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/rslib/src/notes/mod.rs b/rslib/src/notes/mod.rs index d1ddffbf6..1b5b9c52a 100644 --- a/rslib/src/notes/mod.rs +++ b/rslib/src/notes/mod.rs @@ -534,7 +534,7 @@ impl Collection { /// notetypes, check if there is a cloze in a non-cloze field or if there's /// no cloze at all. For other notetypes, just check if there's a cloze. pub(crate) fn note_fields_check(&mut self, note: &Note) -> Result { - if let Some(text) = note.fields.get(0) { + Ok(if let Some(text) = note.fields.get(0) { let field1 = if self.get_config_bool(BoolKey::NormalizeNoteText) { normalize_to_nfc(text) } else { @@ -542,15 +542,20 @@ impl Collection { }; let stripped = strip_html_preserving_media_filenames(&field1); if stripped.trim().is_empty() { - Ok(NoteFieldsState::Empty) - } else if self.is_duplicate(&stripped, note)? { - Ok(NoteFieldsState::Duplicate) + NoteFieldsState::Empty } else { - self.field_cloze_check(note) + let cloze_state = self.field_cloze_check(note)?; + if cloze_state != NoteFieldsState::Normal { + cloze_state + } else if self.is_duplicate(&stripped, note)? { + NoteFieldsState::Duplicate + } else { + NoteFieldsState::Normal + } } } else { - Ok(NoteFieldsState::Empty) - } + NoteFieldsState::Empty + }) } fn is_duplicate(&self, first_field: &str, note: &Note) -> Result {