mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 06:22:22 -04:00
Merge branch 'master' into resethook
This commit is contained in:
commit
dcf82fb403
9 changed files with 64 additions and 51 deletions
|
@ -1565,7 +1565,7 @@ where id in %s"""
|
||||||
newRow = max(newRow, 0)
|
newRow = max(newRow, 0)
|
||||||
self.model.focusedCard = self.model.cards[newRow]
|
self.model.focusedCard = self.model.cards[newRow]
|
||||||
self.model.endReset()
|
self.model.endReset()
|
||||||
self.mw.requireReset(reason=ResetReason.BrowserDeleteNote, context=self)
|
self.mw.reset()
|
||||||
tooltip(
|
tooltip(
|
||||||
ngettext("%d note deleted.", "%d notes deleted.", len(nids)) % len(nids)
|
ngettext("%d note deleted.", "%d notes deleted.", len(nids)) % len(nids)
|
||||||
)
|
)
|
||||||
|
|
|
@ -491,7 +491,7 @@ class Editor:
|
||||||
self.web.eval("setBackgrounds(%s);" % json.dumps(cols))
|
self.web.eval("setBackgrounds(%s);" % json.dumps(cols))
|
||||||
|
|
||||||
def showDupes(self):
|
def showDupes(self):
|
||||||
contents = stripHTMLMedia(self.note.fields[0])
|
contents = html.escape(stripHTMLMedia(self.note.fields[0]))
|
||||||
browser = aqt.dialogs.open("Browser", self.mw)
|
browser = aqt.dialogs.open("Browser", self.mw)
|
||||||
browser.form.searchEdit.lineEdit().setText(
|
browser.form.searchEdit.lineEdit().setText(
|
||||||
'"dupe:%s,%s"' % (self.note.model()["id"], contents)
|
'"dupe:%s,%s"' % (self.note.model()["id"], contents)
|
||||||
|
|
|
@ -907,6 +907,7 @@ title="%s" %s>%s</button>""" % (
|
||||||
self.media_syncer.start()
|
self.media_syncer.start()
|
||||||
|
|
||||||
def on_collection_sync_finished():
|
def on_collection_sync_finished():
|
||||||
|
self.col.clearUndo()
|
||||||
self.reset()
|
self.reset()
|
||||||
after_sync()
|
after_sync()
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub(super) fn open_or_create<P: AsRef<Path>>(path: P) -> Result<Connection> {
|
||||||
|
|
||||||
db.pragma_update(None, "page_size", &4096)?;
|
db.pragma_update(None, "page_size", &4096)?;
|
||||||
db.pragma_update(None, "legacy_file_format", &false)?;
|
db.pragma_update(None, "legacy_file_format", &false)?;
|
||||||
db.pragma_update(None, "journal", &"wal")?;
|
db.pragma_update_and_check(None, "journal_mode", &"wal", |_| Ok(()))?;
|
||||||
|
|
||||||
initial_db_setup(&mut db)?;
|
initial_db_setup(&mut db)?;
|
||||||
|
|
||||||
|
|
|
@ -274,6 +274,8 @@ impl Collection {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return Err(AnkiError::NotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.transact(None, |col| {
|
self.transact(None, |col| {
|
||||||
|
|
|
@ -163,15 +163,26 @@ impl NoteType {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adjust sort index to match repositioned fields.
|
||||||
fn reposition_sort_idx(&mut self) {
|
fn reposition_sort_idx(&mut self) {
|
||||||
let adjusted_idx = self.fields.iter().enumerate().find_map(|(idx, f)| {
|
self.config.sort_field_idx = self
|
||||||
if f.ord == Some(self.config.sort_field_idx) {
|
.fields
|
||||||
Some(idx)
|
.iter()
|
||||||
} else {
|
.enumerate()
|
||||||
None
|
.find_map(|(idx, f)| {
|
||||||
}
|
if f.ord == Some(self.config.sort_field_idx) {
|
||||||
});
|
Some(idx as u32)
|
||||||
self.config.sort_field_idx = adjusted_idx.unwrap_or(0) as u32;
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
// provided ordinal not on any existing field; cap to bounds
|
||||||
|
self.config
|
||||||
|
.sort_field_idx
|
||||||
|
.max(0)
|
||||||
|
.min((self.fields.len() - 1) as u32)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn normalize_names(&mut self) {
|
pub(crate) fn normalize_names(&mut self) {
|
||||||
|
|
|
@ -9,11 +9,9 @@ use crate::{
|
||||||
err::Result,
|
err::Result,
|
||||||
notes::field_checksum,
|
notes::field_checksum,
|
||||||
notetype::NoteTypeID,
|
notetype::NoteTypeID,
|
||||||
text::matches_wildcard,
|
text::{matches_wildcard, text_to_re},
|
||||||
text::{normalize_to_nfc, strip_html_preserving_image_filenames, without_combining},
|
text::{normalize_to_nfc, strip_html_preserving_image_filenames, without_combining},
|
||||||
};
|
};
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use regex::{Captures, Regex};
|
|
||||||
use std::{borrow::Cow, fmt::Write};
|
use std::{borrow::Cow, fmt::Write};
|
||||||
|
|
||||||
pub(crate) struct SqlWriter<'a> {
|
pub(crate) struct SqlWriter<'a> {
|
||||||
|
@ -458,38 +456,6 @@ fn glob_to_re(glob: &str) -> Option<String> {
|
||||||
Some(text_to_re(glob))
|
Some(text_to_re(glob))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Escape text, converting glob characters to regex syntax, then return.
|
|
||||||
fn text_to_re(glob: &str) -> String {
|
|
||||||
lazy_static! {
|
|
||||||
static ref ESCAPED: Regex = Regex::new(r"(\\\\)?\\\*").unwrap();
|
|
||||||
static ref GLOB: Regex = Regex::new(r"(\\\\)?[_%]").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let escaped = regex::escape(glob);
|
|
||||||
|
|
||||||
let text = ESCAPED.replace_all(&escaped, |caps: &Captures| {
|
|
||||||
if caps.get(0).unwrap().as_str().len() == 2 {
|
|
||||||
".*"
|
|
||||||
} else {
|
|
||||||
r"\*"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let text2 = GLOB.replace_all(&text, |caps: &Captures| {
|
|
||||||
match caps.get(0).unwrap().as_str() {
|
|
||||||
"_" => ".",
|
|
||||||
"%" => ".*",
|
|
||||||
other => {
|
|
||||||
// strip off the escaping char
|
|
||||||
&other[2..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.to_string()
|
|
||||||
});
|
|
||||||
|
|
||||||
text2.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
pub enum RequiredTable {
|
pub enum RequiredTable {
|
||||||
Notes,
|
Notes,
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::{
|
||||||
collection::Collection,
|
collection::Collection,
|
||||||
err::{AnkiError, Result},
|
err::{AnkiError, Result},
|
||||||
notes::{NoteID, TransformNoteOutput},
|
notes::{NoteID, TransformNoteOutput},
|
||||||
|
text::text_to_re,
|
||||||
{text::normalize_to_nfc, types::Usn},
|
{text::normalize_to_nfc, types::Usn},
|
||||||
};
|
};
|
||||||
use regex::{NoExpand, Regex, Replacer};
|
use regex::{NoExpand, Regex, Replacer};
|
||||||
|
@ -123,11 +124,7 @@ impl Collection {
|
||||||
// generate regexps
|
// generate regexps
|
||||||
let tags = split_tags(tags)
|
let tags = split_tags(tags)
|
||||||
.map(|tag| {
|
.map(|tag| {
|
||||||
let tag = if regex {
|
let tag = if regex { tag.into() } else { text_to_re(tag) };
|
||||||
tag.into()
|
|
||||||
} else {
|
|
||||||
regex::escape(tag)
|
|
||||||
};
|
|
||||||
Regex::new(&format!("(?i){}", tag))
|
Regex::new(&format!("(?i){}", tag))
|
||||||
.map_err(|_| AnkiError::invalid_input("invalid regex"))
|
.map_err(|_| AnkiError::invalid_input("invalid regex"))
|
||||||
})
|
})
|
||||||
|
@ -234,6 +231,10 @@ mod test {
|
||||||
let note = col.storage.get_note(note.id)?.unwrap();
|
let note = col.storage.get_note(note.id)?.unwrap();
|
||||||
assert_eq!(note.tags[0], "bar");
|
assert_eq!(note.tags[0], "bar");
|
||||||
|
|
||||||
|
col.replace_tags_for_notes(&[note.id], "b*r", "baz", false)?;
|
||||||
|
let note = col.storage.get_note(note.id)?.unwrap();
|
||||||
|
assert_eq!(note.tags[0], "baz");
|
||||||
|
|
||||||
col.replace_tags_for_notes(&[note.id], "b.r", "baz", true)?;
|
col.replace_tags_for_notes(&[note.id], "b.r", "baz", true)?;
|
||||||
let note = col.storage.get_note(note.id)?.unwrap();
|
let note = col.storage.get_note(note.id)?.unwrap();
|
||||||
assert_eq!(note.tags[0], "baz");
|
assert_eq!(note.tags[0], "baz");
|
||||||
|
|
|
@ -261,6 +261,38 @@ pub(crate) fn without_combining(s: &str) -> Cow<str> {
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Escape text, converting glob characters to regex syntax, then return.
|
||||||
|
pub(crate) fn text_to_re(glob: &str) -> String {
|
||||||
|
lazy_static! {
|
||||||
|
static ref ESCAPED: Regex = Regex::new(r"(\\\\)?\\\*").unwrap();
|
||||||
|
static ref GLOB: Regex = Regex::new(r"(\\\\)?[_%]").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let escaped = regex::escape(glob);
|
||||||
|
|
||||||
|
let text = ESCAPED.replace_all(&escaped, |caps: &Captures| {
|
||||||
|
if caps.get(0).unwrap().as_str().len() == 2 {
|
||||||
|
".*"
|
||||||
|
} else {
|
||||||
|
r"\*"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let text2 = GLOB.replace_all(&text, |caps: &Captures| {
|
||||||
|
match caps.get(0).unwrap().as_str() {
|
||||||
|
"_" => ".",
|
||||||
|
"%" => ".*",
|
||||||
|
other => {
|
||||||
|
// strip off the escaping char
|
||||||
|
&other[2..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
});
|
||||||
|
|
||||||
|
text2.into()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::matches_wildcard;
|
use super::matches_wildcard;
|
||||||
|
|
Loading…
Reference in a new issue