mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
Use backend filters instead of literal searches
This commit is contained in:
parent
9ef691c06f
commit
fda2bfdb4e
2 changed files with 59 additions and 61 deletions
|
@ -46,6 +46,7 @@ TagUsnTuple = pb.TagUsnTuple
|
||||||
NoteType = pb.NoteType
|
NoteType = pb.NoteType
|
||||||
DeckTreeNode = pb.DeckTreeNode
|
DeckTreeNode = pb.DeckTreeNode
|
||||||
StockNoteType = pb.StockNoteType
|
StockNoteType = pb.StockNoteType
|
||||||
|
NamedFilter = pb.FilterToSearchIn.NamedFilter
|
||||||
ConcatSeparator = pb.ConcatenateSearchesIn.Separator
|
ConcatSeparator = pb.ConcatenateSearchesIn.Separator
|
||||||
SyncAuth = pb.SyncAuth
|
SyncAuth = pb.SyncAuth
|
||||||
SyncOutput = pb.SyncCollectionOut
|
SyncOutput = pb.SyncCollectionOut
|
||||||
|
@ -261,6 +262,12 @@ class RustBackend(RustBackendGenerated):
|
||||||
err.ParseFromString(err_bytes)
|
err.ParseFromString(err_bytes)
|
||||||
raise proto_exception_to_native(err)
|
raise proto_exception_to_native(err)
|
||||||
|
|
||||||
|
def filters_to_searches(self, **kwargs) -> List[str]:
|
||||||
|
return [
|
||||||
|
self.filter_to_search(pb.FilterToSearchIn(**dict([f])))
|
||||||
|
for f in kwargs.items()
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def translate_string_in(
|
def translate_string_in(
|
||||||
key: TRValue, **kwargs: Union[str, int, float]
|
key: TRValue, **kwargs: Union[str, int, float]
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import html
|
import html
|
||||||
import re
|
|
||||||
import time
|
import time
|
||||||
from concurrent.futures import Future
|
from concurrent.futures import Future
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
@ -21,7 +20,7 @@ from anki.consts import *
|
||||||
from anki.lang import without_unicode_isolation
|
from anki.lang import without_unicode_isolation
|
||||||
from anki.models import NoteType
|
from anki.models import NoteType
|
||||||
from anki.notes import Note
|
from anki.notes import Note
|
||||||
from anki.rsbackend import ConcatSeparator, DeckTreeNode, InvalidInput
|
from anki.rsbackend import ConcatSeparator, DeckTreeNode, InvalidInput, NamedFilter
|
||||||
from anki.stats import CardStats
|
from anki.stats import CardStats
|
||||||
from anki.utils import htmlToTextLine, ids2str, isMac, isWin
|
from anki.utils import htmlToTextLine, ids2str, isMac, isWin
|
||||||
from aqt import AnkiQt, gui_hooks
|
from aqt import AnkiQt, gui_hooks
|
||||||
|
@ -1110,14 +1109,14 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
item = SidebarItem(
|
item = SidebarItem(
|
||||||
tr(TR.BROWSING_WHOLE_COLLECTION),
|
tr(TR.BROWSING_WHOLE_COLLECTION),
|
||||||
":/icons/collection.svg",
|
":/icons/collection.svg",
|
||||||
self._filterFunc(""),
|
self._filterFunc(name=NamedFilter.WHOLE_COLLECTION),
|
||||||
item_type=SidebarItemType.COLLECTION,
|
item_type=SidebarItemType.COLLECTION,
|
||||||
)
|
)
|
||||||
root.addChild(item)
|
root.addChild(item)
|
||||||
item = SidebarItem(
|
item = SidebarItem(
|
||||||
tr(TR.BROWSING_CURRENT_DECK),
|
tr(TR.BROWSING_CURRENT_DECK),
|
||||||
":/icons/deck.svg",
|
":/icons/deck.svg",
|
||||||
self._filterFunc("deck:current"),
|
self._filterFunc(name=NamedFilter.CURRENT_DECK),
|
||||||
item_type=SidebarItemType.CURRENT_DECK,
|
item_type=SidebarItemType.CURRENT_DECK,
|
||||||
)
|
)
|
||||||
root.addChild(item)
|
root.addChild(item)
|
||||||
|
@ -1140,7 +1139,7 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
item = SidebarItem(
|
item = SidebarItem(
|
||||||
t,
|
t,
|
||||||
":/icons/tag.svg",
|
":/icons/tag.svg",
|
||||||
lambda t=t: self.setFilter("tag", t), # type: ignore
|
lambda t=t: self.setFilter(tag=t), # type: ignore
|
||||||
item_type=SidebarItemType.TAG,
|
item_type=SidebarItemType.TAG,
|
||||||
)
|
)
|
||||||
root.addChild(item)
|
root.addChild(item)
|
||||||
|
@ -1153,7 +1152,7 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
|
|
||||||
def set_filter():
|
def set_filter():
|
||||||
full_name = head + node.name # pylint: disable=cell-var-from-loop
|
full_name = head + node.name # pylint: disable=cell-var-from-loop
|
||||||
return lambda: self.setFilter("deck", full_name)
|
return lambda: self.setFilter(deck=full_name)
|
||||||
|
|
||||||
def toggle_expand():
|
def toggle_expand():
|
||||||
did = node.deck_id # pylint: disable=cell-var-from-loop
|
did = node.deck_id # pylint: disable=cell-var-from-loop
|
||||||
|
@ -1180,7 +1179,7 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
item = SidebarItem(
|
item = SidebarItem(
|
||||||
m.name,
|
m.name,
|
||||||
":/icons/notetype.svg",
|
":/icons/notetype.svg",
|
||||||
lambda m=m: self.setFilter("note", m.name), # type: ignore
|
lambda m=m: self.setFilter(note=m.name), # type: ignore
|
||||||
item_type=SidebarItemType.NOTETYPE,
|
item_type=SidebarItemType.NOTETYPE,
|
||||||
)
|
)
|
||||||
root.addChild(item)
|
root.addChild(item)
|
||||||
|
@ -1208,47 +1207,38 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
|
|
||||||
ml.popupOver(self.form.filter)
|
ml.popupOver(self.form.filter)
|
||||||
|
|
||||||
def setFilter(self, *args):
|
def setFilter(self, *args, **kwargs):
|
||||||
if len(args) == 1:
|
# args are literal searches, kwargs are searches provided by the backend
|
||||||
txt = args[0]
|
|
||||||
else:
|
|
||||||
txt = ""
|
|
||||||
items = []
|
|
||||||
for i, a in enumerate(args):
|
|
||||||
if i % 2 == 0:
|
|
||||||
txt += a + ":"
|
|
||||||
else:
|
|
||||||
txt += re.sub(r'["*_\\]', r"\\\g<0>", a)
|
|
||||||
txt = '"{}"'.format(txt.replace('"', '\\"'))
|
|
||||||
items.append(txt)
|
|
||||||
txt = ""
|
|
||||||
txt = " AND ".join(items)
|
|
||||||
try:
|
try:
|
||||||
if self.mw.app.keyboardModifiers() & Qt.AltModifier:
|
filters = self.col.backend.filters_to_searches(**kwargs)
|
||||||
txt = self.col.backend.negate_search(txt)
|
search = self.col.backend.concatenate_searches(
|
||||||
|
sep=ConcatSeparator.AND, searches=list(args) + filters
|
||||||
|
)
|
||||||
|
mods = self.mw.app.keyboardModifiers()
|
||||||
|
if mods & Qt.AltModifier:
|
||||||
|
search = self.col.backend.negate_search(search)
|
||||||
cur = str(self.form.searchEdit.lineEdit().text())
|
cur = str(self.form.searchEdit.lineEdit().text())
|
||||||
if cur != self._searchPrompt:
|
if cur != self._searchPrompt:
|
||||||
mods = self.mw.app.keyboardModifiers()
|
|
||||||
if mods & Qt.ControlModifier and mods & Qt.ShiftModifier:
|
if mods & Qt.ControlModifier and mods & Qt.ShiftModifier:
|
||||||
txt = self.col.backend.replace_search_term(
|
search = self.col.backend.replace_search_term(
|
||||||
search=cur, replacement=txt
|
search=cur, replacement=search
|
||||||
)
|
)
|
||||||
elif mods & Qt.ControlModifier:
|
elif mods & Qt.ControlModifier:
|
||||||
txt = self.col.backend.concatenate_searches(
|
search = self.col.backend.concatenate_searches(
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
sep=ConcatSeparator.AND,
|
sep=ConcatSeparator.AND,
|
||||||
searches=[cur, txt],
|
searches=[cur, search],
|
||||||
)
|
)
|
||||||
elif mods & Qt.ShiftModifier:
|
elif mods & Qt.ShiftModifier:
|
||||||
txt = self.col.backend.concatenate_searches(
|
search = self.col.backend.concatenate_searches(
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
sep=ConcatSeparator.OR,
|
sep=ConcatSeparator.OR,
|
||||||
searches=[cur, txt],
|
searches=[cur, search],
|
||||||
)
|
)
|
||||||
except InvalidInput as e:
|
except InvalidInput as e:
|
||||||
showWarning(str(e))
|
showWarning(str(e))
|
||||||
else:
|
else:
|
||||||
self.form.searchEdit.lineEdit().setText(txt)
|
self.form.searchEdit.lineEdit().setText(search)
|
||||||
self.onSearchActivated()
|
self.onSearchActivated()
|
||||||
|
|
||||||
def _simpleFilters(self, items):
|
def _simpleFilters(self, items):
|
||||||
|
@ -1257,18 +1247,21 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
if row is None:
|
if row is None:
|
||||||
ml.addSeparator()
|
ml.addSeparator()
|
||||||
else:
|
else:
|
||||||
label, filter = row
|
label, filter_name = row
|
||||||
ml.addItem(label, self._filterFunc(filter))
|
ml.addItem(label, self._filterFunc(name=filter_name))
|
||||||
return ml
|
return ml
|
||||||
|
|
||||||
def _filterFunc(self, *args):
|
def _filterFunc(self, *args, **kwargs):
|
||||||
return lambda *, f=args: self.setFilter(*f)
|
return lambda: self.setFilter(*args, **kwargs)
|
||||||
|
|
||||||
def _commonFilters(self):
|
def _commonFilters(self):
|
||||||
return self._simpleFilters(
|
return self._simpleFilters(
|
||||||
(
|
(
|
||||||
(tr(TR.BROWSING_WHOLE_COLLECTION), ""),
|
(tr(TR.BROWSING_WHOLE_COLLECTION), NamedFilter.WHOLE_COLLECTION),
|
||||||
(tr(TR.BROWSING_CURRENT_DECK), '"deck:current"'),
|
(
|
||||||
|
tr(TR.BROWSING_CURRENT_DECK),
|
||||||
|
NamedFilter.CURRENT_DECK,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1277,9 +1270,9 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
subm.addChild(
|
subm.addChild(
|
||||||
self._simpleFilters(
|
self._simpleFilters(
|
||||||
(
|
(
|
||||||
(tr(TR.BROWSING_ADDED_TODAY), '"added:1"'),
|
(tr(TR.BROWSING_ADDED_TODAY), NamedFilter.ADDED_TODAY),
|
||||||
(tr(TR.BROWSING_STUDIED_TODAY), '"rated:1"'),
|
(tr(TR.BROWSING_STUDIED_TODAY), NamedFilter.STUDIED_TODAY),
|
||||||
(tr(TR.BROWSING_AGAIN_TODAY), '"rated:1:1"'),
|
(tr(TR.BROWSING_AGAIN_TODAY), NamedFilter.AGAIN_TODAY),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1290,20 +1283,20 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
subm.addChild(
|
subm.addChild(
|
||||||
self._simpleFilters(
|
self._simpleFilters(
|
||||||
(
|
(
|
||||||
(tr(TR.ACTIONS_NEW), '"is:new"'),
|
(tr(TR.ACTIONS_NEW), NamedFilter.NEW),
|
||||||
(tr(TR.SCHEDULING_LEARNING), '"is:learn"'),
|
(tr(TR.SCHEDULING_LEARNING), NamedFilter.LEARN),
|
||||||
(tr(TR.SCHEDULING_REVIEW), '"is:review"'),
|
(tr(TR.SCHEDULING_REVIEW), NamedFilter.REVIEW),
|
||||||
(tr(TR.FILTERING_IS_DUE), '"is:due"'),
|
(tr(TR.FILTERING_IS_DUE), NamedFilter.DUE),
|
||||||
None,
|
None,
|
||||||
(tr(TR.BROWSING_SUSPENDED), '"is:suspended"'),
|
(tr(TR.BROWSING_SUSPENDED), NamedFilter.SUSPENDED),
|
||||||
(tr(TR.BROWSING_BURIED), '"is:buried"'),
|
(tr(TR.BROWSING_BURIED), NamedFilter.BURIED),
|
||||||
None,
|
None,
|
||||||
(tr(TR.ACTIONS_RED_FLAG), '"flag:1"'),
|
(tr(TR.ACTIONS_RED_FLAG), NamedFilter.RED_FLAG),
|
||||||
(tr(TR.ACTIONS_ORANGE_FLAG), '"flag:2"'),
|
(tr(TR.ACTIONS_ORANGE_FLAG), NamedFilter.ORANGE_FLAG),
|
||||||
(tr(TR.ACTIONS_GREEN_FLAG), '"flag:3"'),
|
(tr(TR.ACTIONS_GREEN_FLAG), NamedFilter.GREEN_FLAG),
|
||||||
(tr(TR.ACTIONS_BLUE_FLAG), '"flag:4"'),
|
(tr(TR.ACTIONS_BLUE_FLAG), NamedFilter.BLUE_FLAG),
|
||||||
(tr(TR.BROWSING_NO_FLAG), '"flag:0"'),
|
(tr(TR.BROWSING_NO_FLAG), NamedFilter.NO_FLAG),
|
||||||
(tr(TR.BROWSING_ANY_FLAG), '"-flag:0"'),
|
(tr(TR.BROWSING_ANY_FLAG), NamedFilter.ANY_FLAG),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1320,7 +1313,7 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
|
|
||||||
tagList = MenuList()
|
tagList = MenuList()
|
||||||
for t in sorted(self.col.tags.all(), key=lambda s: s.lower()):
|
for t in sorted(self.col.tags.all(), key=lambda s: s.lower()):
|
||||||
tagList.addItem(self._escapeMenuItem(t), self._filterFunc("tag", t))
|
tagList.addItem(self._escapeMenuItem(t), self._filterFunc(tag=t))
|
||||||
|
|
||||||
m.addChild(tagList.chunked())
|
m.addChild(tagList.chunked())
|
||||||
return m
|
return m
|
||||||
|
@ -1333,13 +1326,11 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
fullname = parent_prefix + node.name
|
fullname = parent_prefix + node.name
|
||||||
if node.children:
|
if node.children:
|
||||||
subm = parent.addMenu(escaped_name)
|
subm = parent.addMenu(escaped_name)
|
||||||
subm.addItem(
|
subm.addItem(tr(TR.ACTIONS_FILTER), self._filterFunc(deck=fullname))
|
||||||
tr(TR.ACTIONS_FILTER), self._filterFunc("deck", fullname)
|
|
||||||
)
|
|
||||||
subm.addSeparator()
|
subm.addSeparator()
|
||||||
addDecks(subm, node.children, fullname + "::")
|
addDecks(subm, node.children, fullname + "::")
|
||||||
else:
|
else:
|
||||||
parent.addItem(escaped_name, self._filterFunc("deck", fullname))
|
parent.addItem(escaped_name, self._filterFunc(deck=fullname))
|
||||||
|
|
||||||
alldecks = self.col.decks.deck_tree()
|
alldecks = self.col.decks.deck_tree()
|
||||||
ml = MenuList()
|
ml = MenuList()
|
||||||
|
@ -1361,12 +1352,12 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
escaped_nt_name = self._escapeMenuItem(nt["name"])
|
escaped_nt_name = self._escapeMenuItem(nt["name"])
|
||||||
# no sub menu if it's a single template
|
# no sub menu if it's a single template
|
||||||
if len(nt["tmpls"]) == 1:
|
if len(nt["tmpls"]) == 1:
|
||||||
noteTypes.addItem(escaped_nt_name, self._filterFunc("note", nt["name"]))
|
noteTypes.addItem(escaped_nt_name, self._filterFunc(note=nt["name"]))
|
||||||
else:
|
else:
|
||||||
subm = noteTypes.addMenu(escaped_nt_name)
|
subm = noteTypes.addMenu(escaped_nt_name)
|
||||||
|
|
||||||
subm.addItem(
|
subm.addItem(
|
||||||
tr(TR.BROWSING_ALL_CARD_TYPES), self._filterFunc("note", nt["name"])
|
tr(TR.BROWSING_ALL_CARD_TYPES), self._filterFunc(note=nt["name"])
|
||||||
)
|
)
|
||||||
subm.addSeparator()
|
subm.addSeparator()
|
||||||
|
|
||||||
|
@ -1380,7 +1371,7 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
name=self._escapeMenuItem(tmpl["name"]),
|
name=self._escapeMenuItem(tmpl["name"]),
|
||||||
)
|
)
|
||||||
subm.addItem(
|
subm.addItem(
|
||||||
name, self._filterFunc("note", nt["name"], "card", str(c + 1))
|
name, self._filterFunc(note=nt["name"], template=c + 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
m.addChild(noteTypes.chunked())
|
m.addChild(noteTypes.chunked())
|
||||||
|
|
Loading…
Reference in a new issue