mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 06:52:21 -04:00
Merge pull request #923 from RumovZ/fix-write-dupe
Fix writing dupe node and escape issues
This commit is contained in:
commit
23bc7ac892
8 changed files with 47 additions and 12 deletions
|
@ -48,6 +48,8 @@ DeckTreeNode = pb.DeckTreeNode
|
|||
StockNoteType = pb.StockNoteType
|
||||
FilterToSearchIn = pb.FilterToSearchIn
|
||||
NamedFilter = pb.FilterToSearchIn.NamedFilter
|
||||
DupeIn = pb.FilterToSearchIn.DupeIn
|
||||
BackendNoteTypeID = pb.NoteTypeID
|
||||
ConcatSeparator = pb.ConcatenateSearchesIn.Separator
|
||||
SyncAuth = pb.SyncAuth
|
||||
SyncOutput = pb.SyncCollectionOut
|
||||
|
|
|
@ -21,8 +21,10 @@ from anki.lang import without_unicode_isolation
|
|||
from anki.models import NoteType
|
||||
from anki.notes import Note
|
||||
from anki.rsbackend import (
|
||||
BackendNoteTypeID,
|
||||
ConcatSeparator,
|
||||
DeckTreeNode,
|
||||
DupeIn,
|
||||
FilterToSearchIn,
|
||||
InvalidInput,
|
||||
NamedFilter,
|
||||
|
@ -2016,6 +2018,17 @@ where id in %s"""
|
|||
# Edit: finding dupes
|
||||
######################################################################
|
||||
|
||||
# filter called by the editor
|
||||
def search_dupe(self, mid: int, text: str):
|
||||
self.form.searchEdit.lineEdit().setText(
|
||||
self.col.backend.filter_to_search(
|
||||
FilterToSearchIn(
|
||||
dupe=DupeIn(mid=BackendNoteTypeID(ntid=mid), text=text)
|
||||
)
|
||||
)
|
||||
)
|
||||
self.onSearchActivated()
|
||||
|
||||
def onFindDupes(self):
|
||||
self.editor.saveNow(self._onFindDupes)
|
||||
|
||||
|
|
|
@ -537,12 +537,8 @@ class Editor:
|
|||
self.web.eval("setBackgrounds(%s);" % json.dumps(cols))
|
||||
|
||||
def showDupes(self):
|
||||
contents = self.note.fields[0].replace('"', r"\"")
|
||||
browser = aqt.dialogs.open("Browser", self.mw)
|
||||
browser.form.searchEdit.lineEdit().setText(
|
||||
'"dupe:%s,%s"' % (self.note.model()["id"], contents)
|
||||
)
|
||||
browser.onSearchActivated()
|
||||
browser.search_dupe(self.note.model()["id"], self.note.fields[0])
|
||||
|
||||
def fieldsAreBlank(self, previousNote=None):
|
||||
if not self.note:
|
||||
|
|
|
@ -775,12 +775,17 @@ message FilterToSearchIn {
|
|||
NO_FLAG = 15;
|
||||
ANY_FLAG = 16;
|
||||
}
|
||||
message DupeIn {
|
||||
NoteTypeID mid = 1;
|
||||
string text = 2;
|
||||
}
|
||||
oneof filter {
|
||||
NamedFilter name = 1;
|
||||
string tag = 2;
|
||||
string deck = 3;
|
||||
string note = 4;
|
||||
uint32 template = 5;
|
||||
DupeIn dupe = 6;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -327,6 +327,10 @@ impl From<pb::FilterToSearchIn> for Node<'_> {
|
|||
Filter::Template(u) => {
|
||||
Node::Search(SearchNode::CardTemplate(TemplateKind::Ordinal(u as u16)))
|
||||
}
|
||||
Filter::Dupe(dupe) => Node::Search(SearchNode::Duplicates {
|
||||
note_type_id: dupe.mid.unwrap_or(pb::NoteTypeId { ntid: 0 }).into(),
|
||||
text: dupe.text.into(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -293,8 +293,8 @@ fn search_node_for_text_with_argument<'a>(
|
|||
"is" => parse_state(val)?,
|
||||
"flag" => parse_flag(val)?,
|
||||
"rated" => parse_rated(val)?,
|
||||
"dupe" => parse_dupe(val)?,
|
||||
"resched" => parse_resched(val)?,
|
||||
"dupe" => parse_dupes(val)?,
|
||||
"prop" => parse_prop(val)?,
|
||||
"re" => SearchNode::Regex(unescape_quotes(val)),
|
||||
"nc" => SearchNode::NoCombining(unescape(val)?),
|
||||
|
@ -392,14 +392,14 @@ fn parse_resched(val: &str) -> ParseResult<SearchNode<'static>> {
|
|||
Ok(SearchNode::Rated { days, ease })
|
||||
}
|
||||
|
||||
/// eg dupes:1231,hello
|
||||
fn parse_dupes(val: &str) -> ParseResult<SearchNode> {
|
||||
/// eg dupe:1231,hello
|
||||
fn parse_dupe(val: &str) -> ParseResult<SearchNode> {
|
||||
let mut it = val.splitn(2, ',');
|
||||
let mid: NoteTypeID = it.next().unwrap().parse()?;
|
||||
let text = it.next().ok_or(ParseError {})?;
|
||||
Ok(SearchNode::Duplicates {
|
||||
note_type_id: mid,
|
||||
text: unescape_quotes(text),
|
||||
text: unescape_quotes_and_backslashes(text),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -478,6 +478,15 @@ fn unescape_quotes(s: &str) -> Cow<str> {
|
|||
}
|
||||
}
|
||||
|
||||
/// For non-globs like dupe text without any assumption about the content
|
||||
fn unescape_quotes_and_backslashes(s: &str) -> Cow<str> {
|
||||
if s.contains('"') || s.contains('\\') {
|
||||
s.replace(r#"\""#, "\"").replace(r"\\", r"\").into()
|
||||
} else {
|
||||
s.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Unescape chars with special meaning to the parser.
|
||||
fn unescape(txt: &str) -> ParseResult<Cow<str>> {
|
||||
if is_invalid_escape(txt) {
|
||||
|
|
|
@ -123,7 +123,7 @@ impl SqlWriter<'_> {
|
|||
self.write_single_field(&norm(field), &self.norm_note(text), *is_re)?
|
||||
}
|
||||
SearchNode::Duplicates { note_type_id, text } => {
|
||||
self.write_dupes(*note_type_id, &self.norm_note(text))?
|
||||
self.write_dupe(*note_type_id, &self.norm_note(text))?
|
||||
}
|
||||
SearchNode::Regex(re) => self.write_regex(&self.norm_note(re)),
|
||||
SearchNode::NoCombining(text) => self.write_no_combining(&self.norm_note(text)),
|
||||
|
@ -443,7 +443,7 @@ impl SqlWriter<'_> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_dupes(&mut self, ntid: NoteTypeID, text: &str) -> Result<()> {
|
||||
fn write_dupe(&mut self, ntid: NoteTypeID, text: &str) -> Result<()> {
|
||||
let text_nohtml = strip_html_preserving_media_filenames(text);
|
||||
let csum = field_checksum(text_nohtml.as_ref());
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ fn write_search_node(node: &SearchNode) -> String {
|
|||
NoteType(s) => quote(&format!("note:{}", s)),
|
||||
Rated { days, ease } => write_rated(days, ease),
|
||||
Tag(s) => quote(&format!("tag:{}", s)),
|
||||
Duplicates { note_type_id, text } => quote(&format!("dupes:{},{}", note_type_id, text)),
|
||||
Duplicates { note_type_id, text } => write_dupe(note_type_id, text),
|
||||
State(k) => write_state(k),
|
||||
Flag(u) => format!("\"flag:{}\"", u),
|
||||
NoteIDs(s) => format!("\"nid:{}\"", s),
|
||||
|
@ -163,6 +163,12 @@ fn write_rated(days: &u32, ease: &EaseKind) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// Escape double quotes and backslashes: \"
|
||||
fn write_dupe(note_type_id: &NoteTypeIDType, text: &str) -> String {
|
||||
let esc = text.replace(r"\", r"\\").replace('"', r#"\""#);
|
||||
format!("\"dupe:{},{}\"", note_type_id, esc)
|
||||
}
|
||||
|
||||
fn write_state(kind: &StateKind) -> String {
|
||||
use StateKind::*;
|
||||
format!(
|
||||
|
|
Loading…
Reference in a new issue