mirror of
https://github.com/ankitects/anki.git
synced 2025-09-22 07:52:24 -04:00
support regex searches
This commit is contained in:
parent
2dc1b5c982
commit
d1ebdbdcce
2 changed files with 22 additions and 4 deletions
|
@ -5,7 +5,7 @@ use crate::err::{AnkiError, Result};
|
|||
use crate::types::ObjID;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::{escaped, is_not, tag, take_while1};
|
||||
use nom::character::complete::{char, one_of};
|
||||
use nom::character::complete::{anychar, char, one_of};
|
||||
use nom::character::is_digit;
|
||||
use nom::combinator::{all_consuming, map, map_res};
|
||||
use nom::sequence::{delimited, preceded, tuple};
|
||||
|
@ -77,6 +77,7 @@ pub(super) enum SearchNode<'a> {
|
|||
kind: PropertyKind,
|
||||
},
|
||||
WholeCollection,
|
||||
Regex(Cow<'a, str>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -235,9 +236,8 @@ fn quoted_term_str(s: &str) -> IResult<&str, &str> {
|
|||
}
|
||||
|
||||
/// Quoted text, terminated by a non-escaped double quote
|
||||
/// Can escape %, _, " and \
|
||||
fn quoted_term_inner(s: &str) -> IResult<&str, &str> {
|
||||
escaped(is_not(r#""\"#), '\\', one_of(r#""\%_"#))(s)
|
||||
escaped(is_not(r#""\"#), '\\', anychar)(s)
|
||||
}
|
||||
|
||||
/// eg deck:"foo bar" - quotes must come after the :
|
||||
|
@ -270,7 +270,7 @@ fn search_node_for_text_with_argument<'a>(
|
|||
"rated" => parse_rated(val.as_ref())?,
|
||||
"dupes" => parse_dupes(val.as_ref())?,
|
||||
"prop" => parse_prop(val.as_ref())?,
|
||||
|
||||
"re" => SearchNode::Regex(val),
|
||||
// anything else is a field search
|
||||
_ => SearchNode::SingleField {
|
||||
field: key,
|
||||
|
@ -432,6 +432,12 @@ mod test {
|
|||
]
|
||||
);
|
||||
|
||||
// any character should be escapable in quotes
|
||||
assert_eq!(
|
||||
parse(r#""re:\btest""#)?,
|
||||
vec![Search(Regex(r"\btest".into()))]
|
||||
);
|
||||
|
||||
assert_eq!(parse("added:3")?, vec![Search(AddedInDays(3))]);
|
||||
assert_eq!(
|
||||
parse("card:front")?,
|
||||
|
|
|
@ -80,6 +80,7 @@ impl SqlWriter<'_, '_> {
|
|||
}
|
||||
SearchNode::Property { operator, kind } => self.write_prop(operator, kind)?,
|
||||
SearchNode::WholeCollection => write!(self.sql, "true").unwrap(),
|
||||
SearchNode::Regex(re) => self.write_regex(re.as_ref()),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
@ -334,6 +335,11 @@ impl SqlWriter<'_, '_> {
|
|||
write!(self.sql, "c.id > {}", cutoff).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_regex(&mut self, word: &str) {
|
||||
self.sql.push_str("n.flds regexp ?");
|
||||
self.args.push(format!(r"(?i){}", word));
|
||||
}
|
||||
}
|
||||
|
||||
// Write a list of IDs as '(x,y,...)' into the provided string.
|
||||
|
@ -537,6 +543,12 @@ mod test {
|
|||
"(n.mid in (1581236385345,1581236385346,1581236385347,1581236385344))"
|
||||
);
|
||||
|
||||
// regex
|
||||
assert_eq!(
|
||||
s(ctx, r"re:\bone"),
|
||||
("(n.flds regexp ?)".into(), vec![r"(?i)\bone".into()])
|
||||
);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
|
Loading…
Reference in a new issue