update find&replace in browser

sadly the UI still stutters on large selections - the calls
to get the selected rows from Qt are really slow.
This commit is contained in:
Damien Elmes 2020-05-05 22:14:12 +10:00
parent 8b557ec382
commit 1852e32183
3 changed files with 52 additions and 30 deletions

View File

@ -111,6 +111,10 @@ class DeckIsFilteredError(Exception):
pass pass
class InvalidInput(StringError):
pass
def proto_exception_to_native(err: pb.BackendError) -> Exception: def proto_exception_to_native(err: pb.BackendError) -> Exception:
val = err.WhichOneof("value") val = err.WhichOneof("value")
if val == "interrupted": if val == "interrupted":
@ -126,7 +130,7 @@ def proto_exception_to_native(err: pb.BackendError) -> Exception:
elif val == "template_parse": elif val == "template_parse":
return TemplateError(err.localized) return TemplateError(err.localized)
elif val == "invalid_input": elif val == "invalid_input":
return StringError(err.localized) return InvalidInput(err.localized)
elif val == "json_error": elif val == "json_error":
return StringError(err.localized) return StringError(err.localized)
elif val == "not_found_error": elif val == "not_found_error":
@ -747,7 +751,8 @@ class RustBackend:
def field_names_for_note_ids(self, nids: List[int]) -> Sequence[str]: def field_names_for_note_ids(self, nids: List[int]) -> Sequence[str]:
return self._run_command( return self._run_command(
pb.BackendInput(field_names_for_notes=pb.FieldNamesForNotesIn(nids=nids)) pb.BackendInput(field_names_for_notes=pb.FieldNamesForNotesIn(nids=nids)),
release_gil=True,
).field_names_for_notes.fields ).field_names_for_notes.fields
def find_and_replace( def find_and_replace(
@ -769,7 +774,8 @@ class RustBackend:
match_case=not nocase, match_case=not nocase,
field_name=field_name, field_name=field_name,
) )
) ),
release_gil=True,
).find_and_replace ).find_and_replace

View File

@ -24,7 +24,7 @@ from anki.decks import DeckManager
from anki.lang import _, ngettext from anki.lang import _, ngettext
from anki.models import NoteType from anki.models import NoteType
from anki.notes import Note from anki.notes import Note
from anki.rsbackend import TR, DeckTreeNode from anki.rsbackend import TR, DeckTreeNode, InvalidInput
from anki.utils import htmlToTextLine, ids2str, intTime, isMac, isWin from anki.utils import htmlToTextLine, ids2str, intTime, isMac, isWin
from aqt import AnkiQt, gui_hooks from aqt import AnkiQt, gui_hooks
from aqt.editor import Editor from aqt.editor import Editor
@ -1926,13 +1926,21 @@ update cards set usn=?, mod=?, did=? where id in """
def onFindReplace(self): def onFindReplace(self):
self.editor.saveNow(self._onFindReplace) self.editor.saveNow(self._onFindReplace)
def _onFindReplace(self): def _onFindReplace(self) -> None:
sf = self.selectedNotes() nids = self.selectedNotes()
if not sf: if not nids:
return return
import anki.find import anki.find
fields = anki.find.fieldNamesForNotes(self.mw.col, sf) def find():
return anki.find.fieldNamesForNotes(self.mw.col, nids)
def on_done(fut):
self._on_find_replace_diag(fut.result(), nids)
self.mw.taskman.with_progress(find, on_done, self)
def _on_find_replace_diag(self, fields: List[str], nids: List[int]) -> None:
d = QDialog(self) d = QDialog(self)
frm = aqt.forms.findreplace.Ui_Dialog() frm = aqt.forms.findreplace.Ui_Dialog()
frm.setupUi(d) frm.setupUi(d)
@ -1948,35 +1956,39 @@ update cards set usn=?, mod=?, did=? where id in """
field = None field = None
else: else:
field = fields[frm.field.currentIndex() - 1] field = fields[frm.field.currentIndex() - 1]
search = frm.find.text()
replace = frm.replace.text()
regex = frm.re.isChecked()
nocase = frm.ignoreCase.isChecked()
self.mw.checkpoint(_("Find and Replace")) self.mw.checkpoint(_("Find and Replace"))
self.mw.progress.start() # starts progress dialog as well
self.model.beginReset() self.model.beginReset()
try:
changed = self.col.findReplace( def do_search():
sf, return self.col.findReplace(nids, search, replace, regex, field, nocase)
str(frm.find.text()),
str(frm.replace.text()), def on_done(fut):
frm.re.isChecked(),
field,
frm.ignoreCase.isChecked(),
)
except sre_constants.error:
showInfo(_("Invalid regular expression."), parent=self)
return
else:
self.search() self.search()
self.mw.requireReset() self.mw.requireReset()
finally:
self.model.endReset() self.model.endReset()
self.mw.progress.finish()
total = len(nids)
try:
changed = fut.result()
except InvalidInput as e:
# failed regex
showWarning(str(e))
return
showInfo( showInfo(
ngettext( tr(TR.FINDREPLACE_NOTES_UPDATED, changed=changed, total=total),
"%(a)d of %(b)d note updated", "%(a)d of %(b)d notes updated", len(sf)
)
% {"a": changed, "b": len(sf),},
parent=self, parent=self,
) )
self.mw.taskman.run_in_background(do_search, on_done)
def onFindReplaceHelp(self): def onFindReplaceHelp(self):
openHelp("findreplace") openHelp("findreplace")

View File

@ -0,0 +1,4 @@
findreplace-notes-updated = { $total ->
[one] {$changed} of {$total} note updated
*[other] {$changed} of {$total} notes updated
}