mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
commit
152eaa1798
6 changed files with 63 additions and 29 deletions
|
@ -46,6 +46,7 @@ TagUsnTuple = pb.TagUsnTuple
|
|||
NoteType = pb.NoteType
|
||||
DeckTreeNode = pb.DeckTreeNode
|
||||
StockNoteType = pb.StockNoteType
|
||||
ConcatSeparator = pb.ConcatenateSearchesIn.Separator
|
||||
SyncAuth = pb.SyncAuth
|
||||
SyncOutput = pb.SyncCollectionOut
|
||||
SyncStatus = pb.SyncStatusOut
|
||||
|
|
|
@ -53,6 +53,10 @@ fn want_release_gil(method: u32) -> bool {
|
|||
| BackendMethod::LatestProgress
|
||||
| BackendMethod::SetWantsAbort
|
||||
| BackendMethod::I18nResources
|
||||
| BackendMethod::NormalizeSearch
|
||||
| BackendMethod::NegateSearch
|
||||
| BackendMethod::ConcatenateSearches
|
||||
| BackendMethod::ReplaceSearchTerm
|
||||
)
|
||||
} else {
|
||||
false
|
||||
|
|
|
@ -21,7 +21,7 @@ from anki.consts import *
|
|||
from anki.lang import without_unicode_isolation
|
||||
from anki.models import NoteType
|
||||
from anki.notes import Note
|
||||
from anki.rsbackend import DeckTreeNode, InvalidInput, pb
|
||||
from anki.rsbackend import ConcatSeparator, DeckTreeNode, InvalidInput
|
||||
from anki.stats import CardStats
|
||||
from anki.utils import htmlToTextLine, ids2str, isMac, isWin
|
||||
from aqt import AnkiQt, gui_hooks
|
||||
|
@ -1236,13 +1236,13 @@ QTableView {{ gridline-color: {grid} }}
|
|||
elif mods & Qt.ControlModifier:
|
||||
txt = self.col.backend.concatenate_searches(
|
||||
# pylint: disable=no-member
|
||||
sep=pb.ConcatenateSearchesIn.Separator.AND,
|
||||
sep=ConcatSeparator.AND,
|
||||
searches=[cur, txt],
|
||||
)
|
||||
elif mods & Qt.ShiftModifier:
|
||||
txt = self.col.backend.concatenate_searches(
|
||||
# pylint: disable=no-member
|
||||
sep=pb.ConcatenateSearchesIn.Separator.OR,
|
||||
sep=ConcatSeparator.OR,
|
||||
searches=[cur, txt],
|
||||
)
|
||||
except InvalidInput as e:
|
||||
|
@ -1413,10 +1413,16 @@ QTableView {{ gridline-color: {grid} }}
|
|||
return ml
|
||||
|
||||
def _onSaveFilter(self) -> None:
|
||||
try:
|
||||
filt = self.col.backend.normalize_search(
|
||||
self.form.searchEdit.lineEdit().text()
|
||||
)
|
||||
except InvalidInput as e:
|
||||
showWarning(str(e))
|
||||
else:
|
||||
name = getOnlyText(tr(TR.BROWSING_PLEASE_GIVE_YOUR_FILTER_A_NAME))
|
||||
if not name:
|
||||
return
|
||||
filt = self.form.searchEdit.lineEdit().text()
|
||||
conf = self.col.get_config("savedFilters")
|
||||
conf[name] = filt
|
||||
self.col.set_config("savedFilters", conf)
|
||||
|
@ -1433,7 +1439,15 @@ QTableView {{ gridline-color: {grid} }}
|
|||
# returns name if found
|
||||
def _currentFilterIsSaved(self):
|
||||
filt = self.form.searchEdit.lineEdit().text()
|
||||
try:
|
||||
filt = self.col.backend.normalize_search(filt)
|
||||
except InvalidInput:
|
||||
pass
|
||||
for k, v in self.col.get_config("savedFilters").items():
|
||||
try:
|
||||
v = self.col.backend.normalize_search(v)
|
||||
except InvalidInput:
|
||||
pass
|
||||
if filt == v:
|
||||
return k
|
||||
return None
|
||||
|
|
|
@ -33,7 +33,8 @@ use crate::{
|
|||
sched::cutoff::local_minutes_west_for_stamp,
|
||||
sched::timespan::{answer_button_time, time_span},
|
||||
search::{
|
||||
concatenate_searches, negate_search, normalize_search, replace_search_term, SortMode,
|
||||
concatenate_searches, negate_search, normalize_search, replace_search_term, BoolSeparator,
|
||||
SortMode,
|
||||
},
|
||||
stats::studied_today,
|
||||
sync::{
|
||||
|
@ -273,6 +274,17 @@ impl From<pb::DeckConfigId> for DeckConfID {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<i32> for BoolSeparator {
|
||||
fn from(sep: i32) -> Self {
|
||||
use pb::concatenate_searches_in::Separator;
|
||||
match Separator::from_i32(sep) {
|
||||
Some(Separator::And) => BoolSeparator::And,
|
||||
Some(Separator::Or) => BoolSeparator::Or,
|
||||
None => BoolSeparator::And,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendService for Backend {
|
||||
fn latest_progress(&self, _input: Empty) -> BackendResult<pb::Progress> {
|
||||
let progress = self.progress_state.lock().unwrap().last_progress;
|
||||
|
@ -437,7 +449,7 @@ impl BackendService for Backend {
|
|||
}
|
||||
|
||||
fn concatenate_searches(&self, input: pb::ConcatenateSearchesIn) -> Result<pb::String> {
|
||||
Ok(concatenate_searches(input.sep, &input.searches)?.into())
|
||||
Ok(concatenate_searches(input.sep.into(), &input.searches)?.into())
|
||||
}
|
||||
|
||||
fn replace_search_term(&self, input: pb::ReplaceSearchTermIn) -> Result<pb::String> {
|
||||
|
|
|
@ -5,4 +5,6 @@ mod sqlwriter;
|
|||
mod writer;
|
||||
|
||||
pub use cards::SortMode;
|
||||
pub use writer::{concatenate_searches, negate_search, normalize_search, replace_search_term};
|
||||
pub use writer::{
|
||||
concatenate_searches, negate_search, normalize_search, replace_search_term, BoolSeparator,
|
||||
};
|
||||
|
|
|
@ -2,15 +2,20 @@
|
|||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
use crate::{
|
||||
backend_proto::concatenate_searches_in::Separator,
|
||||
decks::DeckID as DeckIDType,
|
||||
err::{AnkiError, Result},
|
||||
err::Result,
|
||||
notetype::NoteTypeID as NoteTypeIDType,
|
||||
search::parser::{parse, Node, PropertyKind, SearchNode, StateKind, TemplateKind},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use std::mem;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BoolSeparator {
|
||||
And,
|
||||
Or,
|
||||
}
|
||||
|
||||
/// Take an Anki-style search string and convert it into an equivalent
|
||||
/// search string with normalized syntax.
|
||||
pub fn normalize_search(input: &str) -> Result<String> {
|
||||
|
@ -38,11 +43,10 @@ pub fn negate_search(input: &str) -> Result<String> {
|
|||
/// Take arbitrary Anki-style search strings and return their concatenation where they
|
||||
/// are separated by the provided boolean operator.
|
||||
/// Empty searches (whole collection) are left out.
|
||||
pub fn concatenate_searches(sep: i32, searches: &[String]) -> Result<String> {
|
||||
let bool_node = vec![match Separator::from_i32(sep) {
|
||||
Some(Separator::Or) => Node::Or,
|
||||
Some(Separator::And) => Node::And,
|
||||
None => return Err(AnkiError::SearchError(None)),
|
||||
pub fn concatenate_searches(sep: BoolSeparator, searches: &[String]) -> Result<String> {
|
||||
let bool_node = vec![match sep {
|
||||
BoolSeparator::And => Node::And,
|
||||
BoolSeparator::Or => Node::Or,
|
||||
}];
|
||||
Ok(write_nodes(
|
||||
searches
|
||||
|
@ -221,25 +225,22 @@ mod test {
|
|||
fn concatenating() -> Result<()> {
|
||||
assert_eq!(
|
||||
r#""foo" AND "bar""#,
|
||||
concatenate_searches(
|
||||
Separator::And as i32,
|
||||
&["foo".to_string(), "bar".to_string()]
|
||||
)
|
||||
concatenate_searches(BoolSeparator::And, &["foo".to_string(), "bar".to_string()])
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
r#""foo" OR "bar""#,
|
||||
concatenate_searches(
|
||||
Separator::Or as i32,
|
||||
BoolSeparator::Or,
|
||||
&["foo".to_string(), "".to_string(), "bar".to_string()]
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"",
|
||||
concatenate_searches(Separator::Or as i32, &["".to_string()]).unwrap()
|
||||
concatenate_searches(BoolSeparator::Or, &["".to_string()]).unwrap()
|
||||
);
|
||||
assert_eq!("", concatenate_searches(Separator::Or as i32, &[]).unwrap());
|
||||
assert_eq!("", concatenate_searches(BoolSeparator::Or, &[]).unwrap());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue