diff --git a/pylib/tests/test_find.py b/pylib/tests/test_find.py
index 236096572..45c47011c 100644
--- a/pylib/tests/test_find.py
+++ b/pylib/tests/test_find.py
@@ -309,3 +309,12 @@ def test_findDupes():
assert not r
# front isn't dupe
assert col.find_dupes("Front") == []
+
+
+def test_find_cards_with_newlines():
+ col = getEmptyCol()
+ note = col.newNote()
+ note["Front"] = "foo"
+ note["Back"] = "foo
bar"
+ col.addNote(note)
+ assert len(col.find_cards("foo\nbar")) == 1
diff --git a/rslib/src/search/parser.rs b/rslib/src/search/parser.rs
index 33c1a4622..e3771bab5 100644
--- a/rslib/src/search/parser.rs
+++ b/rslib/src/search/parser.rs
@@ -204,7 +204,7 @@ fn group_inner(input: &str) -> IResult<'_, Vec> {
}
fn whitespace0(s: &str) -> IResult<'_, Vec> {
- many0(one_of(" \u{3000}")).parse(s)
+ many0(one_of(" \n\u{3000}")).parse(s)
}
/// Optional leading space, then a (negated) group or text
@@ -248,7 +248,7 @@ fn quoted_term(s: &str) -> IResult<'_, Node> {
/// eg deck:"foo bar" - quotes must come after the :
fn partially_quoted_term(s: &str) -> IResult<'_, Node> {
let (remaining, (key, val)) = separated_pair(
- escaped(is_not("\"(): \u{3000}\\"), '\\', none_of(" \u{3000}")),
+ escaped(is_not("\"(): \n\u{3000}\\"), '\\', none_of(" \n\u{3000}")),
char(':'),
quoted_term_str,
)
@@ -261,7 +261,7 @@ fn partially_quoted_term(s: &str) -> IResult<'_, Node> {
/// Unquoted text, terminated by whitespace or unescaped ", ( or )
fn unquoted_term(s: &str) -> IResult<'_, Node> {
- match escaped(is_not("\"() \u{3000}\\"), '\\', none_of(" \u{3000}"))(s) {
+ match escaped(is_not("\"() \n\u{3000}\\"), '\\', none_of(" \n\u{3000}"))(s) {
Ok((tail, term)) => {
if term.is_empty() {
Err(parse_error(s))
@@ -281,7 +281,7 @@ fn unquoted_term(s: &str) -> IResult<'_, Node> {
provided: format!("\\{c}"),
},
))
- } else if "\"() \u{3000}".contains(s.chars().next().unwrap()) {
+ } else if "\"() \n\u{3000}".contains(s.chars().next().unwrap()) {
Err(parse_error(s))
} else {
// input ends in an odd number of backslashes