Skip to content

Instantly share code, notes, and snippets.

@Juddd
Created August 16, 2019 10:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Juddd/590d7154368b89dc0df8adefbc7ab02b to your computer and use it in GitHub Desktop.
Save Juddd/590d7154368b89dc0df8adefbc7ab02b to your computer and use it in GitHub Desktop.
Add-on for Anki 2.1 to supplement the Mini Format Pack
from anki.hooks import addHook
import os.path
import json
import re
def escape_html_chars(s):
html_escape_table = {
"&": "&",
'"': """,
"'": "'",
">": ">",
"<": "&lt;",
"\n":"<br>"
}
result = "".join(html_escape_table.get(c, c) for c in s)
return result
def wrap_in_tags(editor, tag, class_name=None):
"""
Wrap selected text in a tag, optionally giving it a class.
"""
selection = editor.web.selectedText()
if not selection:
return
selection = escape_html_chars(selection)
# print("before1:",repr(selection))
# selection = selection.replace("\n", "<br>")
# print("after1:", repr(selection))
tag_string_begin = ("<{0} class='{1}'>".format(tag, class_name) if
class_name else "<{0}>".format(tag))
tag_string_end = "</{0}>".format(tag)
html = editor.note.fields[editor.currentField]
if "<li><br /></li>" in html:
# an empty list means trouble, because somehow Anki will also make the
# line in which we want to put a <code> tag a list if we continue
replacement = tag_string_begin + selection + tag_string_end
editor.web.eval("document.execCommand('insertHTML', false, %s);"
% json.dumps(replacement))
editor.web.setFocus()
field=editor.currentField
editor.web.eval("focusField(%d);" % field)
def cb():
html_after = editor.note.fields[field]
if html_after != html:
# you're in luck!
return
else:
# nothing happened :( this is a quirk that has to do with <code> tags following <div> tags
return
editor.saveNow(cb)
# Due to a bug in Anki or BeautifulSoup, we cannot use a simple
# wrap operation like with <a>. So this is a very hackish way of making
# sure that a <code> tag may precede or follow a <div> and that the tag
# won't eat the character immediately preceding or following it
pattern = "@%*!"
len_p = len(pattern)
# first, wrap the selection up in a pattern that the user is unlikely
# to use in its own cards
editor.web.eval("wrap('{0}', '{1}')".format(pattern, pattern[::-1]))
# focus the field, so that changes are saved
# this causes the cursor to go to the end of the field
editor.web.setFocus()
field = editor.currentField
editor.web.eval("focusField(%d);" % field)
# print("selection:", repr(selection))
# if tag == "blockquote":
# selection = selection.replace("\n", "<br>")
def cb1():
html = editor.note.fields[field]
begin = html.find(pattern)
end = html.find(pattern[::-1], begin)
html = (html[:begin] + tag_string_begin + selection + tag_string_end +
html[end + len_p:])
# if tag == "blockquote":
# html.replace()
# delete the current HTML and replace it by our new & improved one
editor.note.fields[field] = html
# reload the note: this is needed on OS X, because it will otherwise
# falsely show that the formatting of the element at the start of
# the HTML has spread across the entire note
editor.loadNote()
# focus the field, so that changes are saved
editor.web.setFocus()
editor.web.eval("focusField(%d);" % field)
editor.saveNow(cb1)
def cb2():
editor.web.setFocus()
editor.web.eval("focusField(%d);" % field)
editor.saveNow(cb2)
def delete_tag(editor,pre_fix,post_fix):
selection = editor.web.selectedText()
if not selection:
return
selection = escape_html_chars(selection)
pattern = "@%*!"
html = editor.note.fields[editor.currentField]
if "<li><br /></li>" in html:
# an empty list means trouble, because somehow Anki will also make the
# line in which we want to put a <code> tag a list if we continue
replacement = pattern + selection + pattern[::-1]
editor.web.eval("document.execCommand('insertHTML', false, %s);"
% json.dumps(replacement))
editor.web.setFocus()
field = editor.currentField
editor.web.eval("focusField(%d);" % field)
def cb():
html_after = editor.note.fields[field]
if html_after != html:
# you're in luck!
return
else:
# nothing happened :( this is a quirk that has to do with <code> tags following <div> tags
return
editor.saveNow(cb)
editor.web.eval("wrap('{0}', '{1}')".format(pattern, pattern[::-1]))
# focus the field, so that changes are saved
# this causes the cursor to go to the end of the field
editor.web.setFocus()
field = editor.currentField
editor.web.eval("focusField(%d);" % field)
def cb1():
html = editor.note.fields[field]
# print("before:",html)
# print("first:",f'{pre_fix}{re.escape(pattern)}')
# print("second:",f'{re.escape(pattern[::-1])}{post_fix}')
# print("delete_before:",html)
#删除pattern[::-1]后面的<br>
html = re.sub(f'(?<={re.escape(pattern[::-1])})(<br>)+','',html)
html = re.sub(f'{pre_fix}{re.escape(pattern)}','',html)
html = re.sub(f'{re.escape(pattern[::-1])}{post_fix}','',html)
# html = html.replace(pre_fix+pattern,'').replace(pattern[::-1]+post_fix,'')
# print("delete_after:", html)
# delete the current HTML and replace it by our new & improved one
editor.note.fields[field] = html
# reload the note: this is needed on OS X, because it will otherwise
# falsely show that the formatting of the element at the start of
# the HTML has spread across the entire note
editor.loadNote()
# focus the field, so that changes are saved
editor.web.setFocus()
editor.web.eval("focusField(%d);" % field)
editor.saveNow(cb1)
def cb2():
editor.web.setFocus()
editor.web.eval("focusField(%d);" % field)
editor.saveNow(cb2)
def wrap_pre(editor):
wrap_in_tags(editor,"pre","myCodeClass")
def wrap_code(editor):
wrap_in_tags(editor,"code","myCodeClass")
def wrap_blockquote(editor):
wrap_in_tags(editor,"blockquote")
def wrap_kbd(editor):
wrap_in_tags(editor, "kbd")
def delete_pre(editor):
delete_tag(editor,'<pre.+?','</pre>')
def delete_blockquote(editor):
delete_tag(editor, '<blockquote>', '</blockquote>')
def addMyButton(buttons, editor):
editor._links['button_wrap_pre'] = wrap_pre
editor._links['button_wrap_code'] = wrap_code
editor._links['button_wrap_blockquote'] = wrap_blockquote
editor._links['button_delete_pre'] = delete_pre
editor._links['button_delete_blockquote'] = delete_blockquote
editor._links['button_wrap_kbd'] = wrap_kbd
[buttons.remove(i) for i in buttons if "Insert code block (ctrl+.)" in i]
return buttons + [editor._addButton(os.path.join(os.path.dirname(__file__),"icons\\pre.png"),"button_wrap_pre","Add pre tag for selected text"),
editor._addButton(os.path.join(os.path.dirname(__file__), "icons\\delete_pre.png"),"button_delete_pre", "Delete pre tag for selected text"),
editor._addButton(os.path.join(os.path.dirname(__file__),"icons\\code.png"), "button_wrap_code", "Add inline tag for selected text"),
editor._addButton(os.path.join(os.path.dirname(__file__),"icons\\blockquote.png"), "button_wrap_blockquote", "Add blockquote tag for selected text"),
editor._addButton(os.path.join(os.path.dirname(__file__),"icons\\delete_blockquote.png"), "button_delete_blockquote", "Delete blockquote tag for selected text"),
editor._addButton(os.path.join(os.path.dirname(__file__),"icons\\keyboard.png"), "button_wrap_kbd", "Add kbd tag for selected text")]
addHook("setupEditorButtons", addMyButton)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment