def myLoadNote(self): self.web.eval(js_code) if self.stealFocus: field = self.currentField else: field = -1 if not self._loaded: # will be loaded when page is ready return data = [] for fld, val in self.note.items(): data.append((fld, self.mw.col.media.escapeImages(val))) ########################################################### sticky = [] model = self.note.model() for fld in model['flds']: sticky.append(fld['sticky']) ########################################################### self.web.eval("setFrozenFields(%s, %s, %d);" % ( json.dumps(data), json.dumps(sticky), field)) self.web.eval("setFonts(%s);" % ( json.dumps(self.fonts()))) self.checkValid() self.widget.show() if self.stealFocus: self.web.setFocus()
def doPaste(self, html, internal, extended=False): html = self._pastePreFilter(html, internal) if extended: extended = "true" else: extended = "false" self.web.eval("pasteHTML(%s, %s, %s);" % ( json.dumps(html), json.dumps(internal), extended))
def _addColVars(db, g, gc, c): db.execute( """ update col set conf = ?, decks = ?, dconf = ?""", json.dumps(c), json.dumps({"1": g}), json.dumps({"1": gc}), )
def dump(self) : fp = Storage(self.word).getPath() + self.word + '.mtp' arr_data = [] for e in self.data : st = e.dumps() arr_data.append(st) arr_data_all = [json.dumps(self.stars), json.dumps(arr_data)] with open(fp, 'w') as f: json.dump(arr_data_all, f)
def mediaChangesZip(self): f = StringIO() z = zipfile.ZipFile(f, "w", compression=zipfile.ZIP_DEFLATED) fnames = [] # meta is list of (fname, zipname), where zipname of None # is a deleted file meta = [] sz = 0 for c, (fname, csum) in enumerate(self.db.execute( "select fname, csum from media where dirty=1" " limit %d"%SYNC_ZIP_COUNT)): fnames.append(fname) normname = unicodedata.normalize("NFC", fname) if csum: self.col.log("+media zip", fname) z.write(fname, str(c)) meta.append((normname, str(c))) sz += os.path.getsize(fname) else: self.col.log("-media zip", fname) meta.append((normname, "")) if sz >= SYNC_ZIP_SIZE: break z.writestr("_meta", json.dumps(meta)) z.close() return f.getvalue(), fnames
def zipAdded(self): "Add files to a zip until over SYNC_ZIP_SIZE/COUNT. Return zip data." f = StringIO() z = zipfile.ZipFile(f, "w", compression=zipfile.ZIP_DEFLATED) sz = 0 cnt = 0 files = {} cur = self.db.execute( "select fname from log where type = ?", MEDIA_ADD) fnames = [] while 1: fname = cur.fetchone() if not fname: # add a flag so the server knows it can clean up z.writestr("_finished", "") break fname = fname[0] fnames.append([fname]) z.write(fname, str(cnt)) files[str(cnt)] = fname sz += os.path.getsize(fname) if sz > SYNC_ZIP_SIZE or cnt > SYNC_ZIP_COUNT: break cnt += 1 z.writestr("_meta", json.dumps(files)) z.close() return f.getvalue(), fnames
def append_kanji_info(): """Append additional information about the kanji of the current card.""" global enabled if not enabled: return done = {} info = "<p></p>" for item in mw.reviewer.card.note().items(): for u in item[1]: # prevent showing same kanji twice: if u not in done: done[u] = 1 if u in kanji_info: # FIXME: I'd like to get rid of the kanji mouseover title, # FIXME:: but there does not seem to be a way to get a # FIXME:: furigana-alike formatting in simple HTML. info += """ <span style="%s" title="%s">%s</span> """ % \ ("color:#000000; font-family:KanjiStrokeOrders; font-size:128px;", kanji_info[u], u) # if there is any info, add it to the buffer if len(info): # I have no idea what I'm doing. Seriously, JavaScript?! mw.reviewer.web.eval("""$("div")[0].innerHTML+=%s;""" % json.dumps(info))
def _showQuestion(self): self._reps += 1 self.state = "question" self.typedAnswer = None c = self.card # grab the question and play audio if c.isEmpty(): q = _("""\ The front of this card is empty. Please run Tools>Empty Cards.""") else: q = c.q() if self.autoplay(c): playFromText(q) # render & update bottom q = self._mungeQA(q) klass = "card card%d" % (c.ord+1) self.web.eval("_updateQA(%s, false, '%s');" % (json.dumps(q), klass)) self._toggleStar() if self._bottomReady: self._showAnswerButton() # if we have a type answer field, focus main web if self.typeCorrect: self.mw.web.setFocus() # user hook runHook('showQuestion')
def exportInto(self, path): # open a zip file z = zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED, allowZip64=True) media = self.doExport(z, path) # media map z.writestr("media", json.dumps(media)) z.close()
def _showQuestion(self): self._reps += 1 self.state = "question" self.typedAnswer = None c = self.card # grab the question and play audio if c.isEmpty(): q = _("""\ The front of this card is empty. Please run Tools>Empty Cards.""") else: q = c.q() if self.autoplay(c): playFromText(q) # render & update bottom q = self._mungeQA(q) q = runFilter("prepareQA", q, c, "reviewQuestion") bodyclass = bodyClass(self.mw.col, c) self.web.eval("_showQuestion(%s,'%s');" % (json.dumps(q), bodyclass)) self._drawFlag() self._drawMark() self._showAnswerButton() # if we have a type answer field, focus main web if self.typeCorrect: self.mw.web.setFocus() # user hook runHook('showQuestion')
def mediatest(self, cmd): self.postVars = dict( k=self.hkey, ) return self._dataOnly( self.req("newMediaTest", io.BytesIO( json.dumps(dict(cmd=cmd)).encode("utf8"))))
def insert_markup_in_field(self, markup, field): """ Put markup in the specified field. """ self.editor_instance.web.eval(""" document.getElementById('f%s').innerHTML = %s; """ % (field, json.dumps(unicode(markup))))
def mediaChanges(self, **kw): self.postVars = dict( sk=self.skey, ) resp = json.loads( self.req("mediaChanges", StringIO(json.dumps(kw)))) return self._dataOnly(resp)
def _migrateModels(self): import anki.models db = self.db times = {} mods = {} for row in db.all( "select id, name from models"): # use only first 31 bits if not old anki id t = abs(row[0]) if t > 4294967296: t >>= 32 assert t > 0 m = anki.models.defaultModel.copy() m['id'] = t m['name'] = row[1] m['mod'] = intTime() m['tags'] = [] m['flds'] = self._fieldsForModel(row[0]) m['tmpls'] = self._templatesForModel(row[0], m['flds']) mods[m['id']] = m db.execute("update notes set mid = ? where mid = ?", t, row[0]) # save and clean up db.execute("update col set models = ?", json.dumps(mods)) db.execute("drop table fieldModels") db.execute("drop table cardModels") db.execute("drop table models")
def save_prefs(prefs): """ Save the preferences to disk, base64 encoded. """ encoded_prefs = base64.b64encode(json.dumps(prefs)) with codecs.open(PrefHelper.get_preference_path(), "w", encoding="utf8") as f: f.write(encoded_prefs)
def mediatest(self, cmd): self.postVars = dict( k=self.hkey, ) return self._dataOnly(json.loads( self.req("newMediaTest", StringIO( json.dumps(dict(cmd=cmd))))))
def _migrateModels(self): import anki.models db = self.db times = {} mods = {} for row in db.all( "select id, name from models"): while 1: t = intTime(1000) if t not in times: times[t] = True break m = anki.models.defaultModel.copy() m['id'] = t m['name'] = row[1] m['mod'] = intTime() m['tags'] = [] m['flds'] = self._fieldsForModel(row[0]) m['tmpls'] = self._templatesForModel(row[0], m['flds']) mods[m['id']] = m db.execute("update notes set mid = ? where mid = ?", t, row[0]) # save and clean up db.execute("update col set models = ?", json.dumps(mods)) db.execute("drop table fieldModels") db.execute("drop table cardModels") db.execute("drop table models")
def flush(self, mod=None): "Flush state to DB, updating mod time." self.mod = intTime(1000) if mod is None else mod self.db.execute( """update col set crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""", self.crt, self.mod, self.scm, self.dty, self._usn, self.ls, json.dumps(self.conf))
def meta(self): ret = self.req( "meta", StringIO(json.dumps(dict(v=SYNC_VER))), badAuthRaises=False) if not ret: # invalid auth return return json.loads(ret)
def loadNote(self): if not self.note: return field = self.currentField if not self._loaded: # will be loaded when page is ready return data = [] for fld, val in self.note.items(): data.append((fld, self.escapeImages(val))) self.web.eval("setFields(%s, %d);" % ( json.dumps(data), field)) self.web.eval("setFonts(%s);" % ( json.dumps(self.fonts()))) self.checkValid() self.widget.show() self.web.setFocus()
def begin(self): self.postVars = dict( k=self.hkey, v="ankidesktop,%s,%s"%(anki.version, platDesc()) ) ret = self._dataOnly(self.req( "begin", io.BytesIO(json.dumps(dict()).encode("utf8")))) self.skey = ret['sk'] return ret
def meta(self): ret = self.req( "meta", StringIO(json.dumps(dict( v=SYNC_VER, cv="ankidesktop,%s,%s"%(anki.version, platDesc())))), badAuthRaises=False) if not ret: # invalid auth return return json.loads(ret)
def hostKey(self, user, pw): "Returns hkey or none if user/pw incorrect." self.postVars = dict() ret = self.req("hostKey", StringIO(json.dumps(dict(u=user, p=pw))), badAuthRaises=False) if not ret: # invalid auth return self.hkey = json.loads(ret)["key"] return self.hkey
def json_dump_and_compress(data): """ Take a string `data` and JSONify it. Return the resultant string, encoded in base64. """ ret = unicode(base64.b64encode(json.dumps(data))) assert isinstance(ret, unicode), "Output `ret` is not Unicode" return ret
def begin(self): self.postVars = dict( k=self.hkey, v="ankidesktop,%s,%s"%(anki.version, platDesc()) ) ret = self._dataOnly(json.loads(self.req( "begin", StringIO(json.dumps(dict()))))) self.skey = ret['sk'] return ret
def myBridge(self, str, _old=None): if str.startswith("autocomplete"): (type, jsonText) = str.split(":", 1) result = json.loads(jsonText) text = self.mungeHTML(result["text"]) # bail out if the user hasn't actually changed the field previous = "%d:%s" % (self.currentField, text) if self.prevAutocomplete == previous: return self.prevAutocomplete = previous if text == "" or len(text) > 500 or self.note is None: self.web.eval("$('.autocomplete').remove();") return field = self.note.model()["flds"][self.currentField] if field["name"] in noAutocompleteFields: field["no_autocomplete"] = True if "no_autocomplete" in field.keys() and field["no_autocomplete"]: return # find a value from the same model and field whose # prefix is what the user typed so far query = "'note:%s' '%s:%s*'" % (self.note.model()["name"], field["name"], text) col = self.note.col res = col.findCards(query, order=True) if len(res) == 0: self.web.eval("$('.autocomplete').remove();") return # pull out the full value value = col.getCard(res[0]).note().fields[self.currentField] escaped = json.dumps(value) self.web.eval( """ $('.autocomplete').remove(); if (currentField) { $('<div class="autocomplete">' + %s + '</div>').click(function () { currentField.focus(); currentField.innerHTML = %s; saveField("key"); }).insertAfter(currentField) } """ % (escaped, escaped) ) else: _old(self, str)
def highlight_code(self): addon_conf = mw.col.conf['syntax_highlighting_conf'] # Do we want line numbers? linenos is either true or false according # to the user's preferences linenos = addon_conf['linenos'] style = get_style_by_name(addon_conf['style']) centerfragments = addon_conf['centerfragments'] selected_text = self.web.selectedText() if selected_text: # Sometimes, self.web.selectedText() contains the unicode character # '\u00A0' (non-breaking space). This character messes with the # formatter for highlighted code. To correct this, we replace all # '\u00A0' characters with regular space characters code = selected_text.replace(u'\u00A0', ' ') else: clipboard = QApplication.clipboard() # Get the code from the clipboard code = clipboard.text() langAlias = self.codeHighlightLangAlias # Select the lexer for the correct language my_lexer = get_lexer_by_name(langAlias, stripall=True) # Tell pygments that we will be generating HTML without CSS. # HTML without CSS may take more space, but it's self contained. my_formatter = HtmlFormatter(linenos=linenos, noclasses=True, font_size=16, style=style) if linenos: if centerfragments: pretty_code = "".join(["<center>", highlight(code, my_lexer, my_formatter), "</center><br>"]) else: pretty_code = "".join([highlight(code, my_lexer, my_formatter), "<br>"]) # TODO: understand why this is neccessary else: if centerfragments: pretty_code = "".join(["<center><table><tbody><tr><td>", highlight(code, my_lexer, my_formatter), "</td></tr></tbody></table></center><br>"]) else: pretty_code = "".join(["<table><tbody><tr><td>", highlight(code, my_lexer, my_formatter), "</td></tr></tbody></table><br>"]) # These two lines insert a piece of HTML in the current cursor position self.web.eval("document.execCommand('inserthtml', false, %s);" % json.dumps(pretty_code))
def hostKey(self, user, pw): "Returns hkey or none if user/pw incorrect." self.postVars = dict() ret = self.req( "hostKey", io.BytesIO(json.dumps(dict(u=user, p=pw)).encode("utf8")), badAuthRaises=False) if not ret: # invalid auth return self.hkey = json.loads(ret.decode("utf8"))['key'] return self.hkey
def exportInto(self, path): # open a zip file z = zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED, allowZip64=True) # if all decks and scheduling included, full export if self.includeSched and not self.did: media = self.exportVerbatim(z) else: # otherwise, filter media = self.exportFiltered(z, path) # media map z.writestr("media", json.dumps(media)) z.close()
def checkValid(self): cols = [] err = None for f in self.note.fields: cols.append("#fff") err = self.note.dupeOrEmpty() if err == 2: cols[0] = "#fcc" self.web.eval("showDupes();") else: self.web.eval("hideDupes();") self.web.eval("setBackgrounds(%s);" % json.dumps(cols))
def _run(self, cmd, data): return json.loads(self.req(cmd, StringIO(json.dumps(data))))
def mediaChanges(self, **kw): self.postVars = dict(sk=self.skey, ) resp = json.loads(self.req("mediaChanges", StringIO(json.dumps(kw)))) return self._dataOnly(resp)
def highlight_code(ed): addon_conf = mw.col.get_config('syntax_highlighting_conf') # Do we want line numbers? linenos is either true or false according # to the user's preferences linenos = addon_conf['linenos'] centerfragments = addon_conf['centerfragments'] # Do we want to use css classes or have formatting directly in HTML? # Using css classes takes up less space and gives the user more # customization options, but is less self-contained as it requires # setting the styling on every note type where code is used noclasses = not addon_conf['cssclasses'] selected_text = ed.web.selectedText() if selected_text: # Sometimes, self.web.selectedText() contains the unicode character # '\u00A0' (non-breaking space). This character messes with the # formatter for highlighted code. To correct this, we replace all # '\u00A0' characters with regular space characters code = selected_text.replace('\u00A0', ' ') else: clipboard = QApplication.clipboard() # Get the code from the clipboard code = clipboard.text() langAlias = ed.codeHighlightLangAlias # Select the lexer for the correct language try: my_lexer = get_lexer_by_name(langAlias, stripall=True) except ClassNotFound as e: print(e) showError(ERR_LEXER, parent=ed.parentWindow) return False # Create html formatter object including flags for line nums and css classes try: my_formatter = HtmlFormatter(linenos=linenos, noclasses=noclasses, font_size=16, style=STYLE) except ClassNotFound as e: print(e) showError(ERR_STYLE, parent=ed.parentWindow) return False if linenos: if centerfragments: pretty_code = "".join([ "<center>", highlight(code, my_lexer, my_formatter), "</center><br>" ]) else: pretty_code = "".join( [highlight(code, my_lexer, my_formatter), "<br>"]) # TODO: understand why this is neccessary else: if centerfragments: pretty_code = "".join([ "<center>", highlight(code, my_lexer, my_formatter), "</center><br>" ]) else: pretty_code = "".join( [highlight(code, my_lexer, my_formatter), "<br>"]) pretty_code = process_html(pretty_code) # These two lines insert a piece of HTML in the current cursor position ed.web.eval("document.execCommand('inserthtml', false, %s);" % json.dumps(pretty_code))
def mediaChanges(self, **kw): self.postVars = dict( sk=self.skey, ) return self._dataOnly( self.req("mediaChanges", io.BytesIO(json.dumps(kw).encode("utf8"))))
def files(self, **kw): return self.req("files", StringIO(json.dumps(kw)))
def mediaSanity(self, **kw): return self._dataOnly( self.req("mediaSanity", io.BytesIO(json.dumps(kw).encode("utf8"))))
def flush(self): "Flush the registry if any models were changed." if self.changed: self.col.db.execute("update col set models = ?", json.dumps(self.models)) self.changed = False
def addMedia(self, path, canDelete=False): html = self._addMedia(path, canDelete) self.web.eval("setFormat('inserthtml', %s);" % json.dumps(html))
def _toggleStar(self): self.web.eval("_toggleStar(%s);" % json.dumps(self.card.note().hasTag("marked")))
def save_prefs(prefs): """ Save the preferences to disk. """ with codecs.open(PrefHelper.get_preference_path(), "w", encoding="utf8") as f: f.write(json.dumps(prefs))
def _showEaseButtons(self): self.bottom.web.setFocus() middle = self._answerButtons() self.bottom.web.eval("showAnswer(%s);" % json.dumps(middle))
def flush(self): if self.changed: self.col.db.execute("update col set decks=?, dconf=?", json.dumps(self.decks), json.dumps(self.dconf)) self.changed = False
def _addColVars(db, g, gc, c): db.execute(""" update col set conf = ?, decks = ?, dconf = ?""", json.dumps(c), json.dumps({'1': g}), json.dumps({'1': gc}))
def wrap_in_tags(self, tag, class_name=None): """ Wrap selected text in a tag, optionally giving it a class. """ selection = self.web.selectedText() if not selection: return selection = utility.escape_html_chars(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 = self.note.fields[self.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 self.web.eval("document.execCommand('insertHTML', false, %s);" % json.dumps(replacement)) self.web.setFocus() self.web.eval("focusField(%d);" % self.currentField) self.saveNow() html_after = self.note.fields[self.currentField] if html_after != html: # you're in luck! return else: # nothing happened :( this is another Anki bug :( that has to do # with <code> tags following <div> tags return # 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 self.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 self.web.setFocus() self.web.eval("focusField(%d);" % self.currentField) self.saveNow() html = self.note.fields[self.currentField] begin = html.find(pattern) end = html.find(pattern[::-1], begin) html = (html[:begin] + tag_string_begin + selection + tag_string_end + html[end + len_p:]) # delete the current HTML and replace it by our new & improved one self.note.fields[self.currentField] = 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 self.loadNote() # focus the field, so that changes are saved self.web.setFocus() self.web.eval("focusField(%d);" % self.currentField) self.saveNow() self.web.setFocus() self.web.eval("focusField(%d);" % self.currentField)
def doPaste(self, html, internal): if not internal: html = self._pastePreFilter(html) self.web.eval("pasteHTML(%s, %s);" % (json.dumps(html), json.dumps(internal)))
def mediatest(self, n): return json.loads( self.req("mediatest", StringIO( json.dumps(dict(n=n)))))
def _drawMark(self): self.web.eval("_drawMark(%s);" % json.dumps(self.card.note().hasTag("marked")))
def remove(self, **kw): return json.loads( self.req("remove", StringIO(json.dumps(kw))))
def mediatest(self, cmd): self.postVars = dict(k=self.hkey, ) return self._dataOnly( json.loads( self.req("newMediaTest", StringIO(json.dumps(dict(cmd=cmd))))))
def downloadFiles(self, **kw): return self.req("downloadFiles", io.BytesIO(json.dumps(kw).encode("utf8")))
def mediaSanity(self, **kw): return self._dataOnly( json.loads(self.req("mediaSanity", StringIO(json.dumps(kw)))))
def _run(self, cmd, data): return json.loads( self.req(cmd, io.BytesIO(json.dumps(data).encode("utf8"))).decode("utf8"))
def downloadFiles(self, **kw): return self.req("downloadFiles", StringIO(json.dumps(kw)))
def hilcd(ed, code, langAlias): global LASTUSED linenos = gc('linenos') centerfragments = gc('centerfragments') noclasses = not gc('cssclasses') if (ed.mw.app.keyboardModifiers() & Qt.ShiftModifier): linenos ^= True if (ed.mw.app.keyboardModifiers() & Qt.ControlModifier): centerfragments ^= True mystyle = gc("style") if (ed.mw.app.keyboardModifiers() & Qt.AltModifier): d = FilterDialog(parent=None, values=list(get_all_styles())) if d.exec(): mystyle = d.selkey noclasses = True inline = False if (ed.mw.app.keyboardModifiers() & Qt.MetaModifier): inline = True if inline: linenos = False try: if gc("remove leading spaces if possible", True): my_lexer = get_lexer_by_name(langAlias, stripall=False) else: my_lexer = get_lexer_by_name(langAlias, stripall=True) except ClassNotFound as e: print(e) print(ERR_LEXER) showError(ERR_LEXER, parent=ed.parentWindow) return False try: # http://pygments.org/docs/formatters/#HtmlFormatter """ nowrap If set to True, don’t wrap the tokens at all, not even inside a <pre> tag. This disables most other options (default: False). full Tells the formatter to output a “full” document, i.e. a complete self-contained document (default: False). title If full is true, the title that should be used to caption the document (default: ''). style The style to use, can be a string or a Style subclass (default: 'default'). This option has no effect if the cssfile and noclobber_cssfile option are given and the file specified in cssfile exists. noclasses If set to true, token <span> tags (as well as line number elements) will not use CSS classes, but inline styles. This is not recommended for larger pieces of code since it increases output size by quite a bit (default: False). classprefix Since the token types use relatively short class names, they may clash with some of your own class names. In this case you can use the classprefix option to give a string to prepend to all Pygments-generated CSS class names for token types. Note that this option also affects the output of get_style_defs(). cssclass CSS class for the wrapping <div> tag (default: 'highlight'). If you set this option, the default selector for get_style_defs() will be this class. If you select the 'table' line numbers, the wrapping table will have a CSS class of this string plus 'table', the default is accordingly 'highlighttable'. cssstyles Inline CSS styles for the wrapping <div> tag (default: ''). prestyles Inline CSS styles for the <pre> tag (default: ''). cssfile If the full option is true and this option is given, it must be the name of an external file. If the filename does not include an absolute path, the file’s path will be assumed to be relative to the main output file’s path, if the latter can be found. The stylesheet is then written to this file instead of the HTML file. noclobber_cssfile If cssfile is given and the specified file exists, the css file will not be overwritten. This allows the use of the full option in combination with a user specified css file. Default is False. linenos If set to 'table', output line numbers as a table with two cells, one containing the line numbers, the other the whole code. This is copy-and-paste-friendly, but may cause alignment problems with some browsers or fonts. If set to 'inline', the line numbers will be integrated in the <pre> tag that contains the code The default value is False, which means no line numbers at all. For compatibility with Pygments 0.7 and earlier, every true value except 'inline' means the same as 'table' (in particular, that means also True). hl_lines Specify a list of lines to be highlighted. linenostart The line number for the first line (default: 1). linenostep If set to a number n > 1, only every nth line number is printed. linenospecial If set to a number n > 0, every nth line number is given the CSS class "special" (default: 0). nobackground If set to True, the formatter won’t output the background color for the wrapping element (this automatically defaults to False when there is no wrapping element [eg: no argument for the get_syntax_defs method given]) (default: False). lineseparator This string is output between lines of code. It defaults to "\n", which is enough to break a line inside <pre> tags, but you can e.g. set it to "<br>" to get HTML line breaks. lineanchors If set to a nonempty string, e.g. foo, the formatter will wrap each output line in an anchor tag with a name of foo-linenumber. This allows easy linking to certain lines. linespans If set to a nonempty string, e.g. foo, the formatter will wrap each output line in a span tag with an id of foo-linenumber. This allows easy access to lines via javascript. anchorlinenos If set to True, will wrap line numbers in <a> tags. Used in combination with linenos and lineanchors. tagsfile If set to the path of a ctags file, wrap names in anchor tags that link to their definitions. lineanchors should be used, and the tags file should specify line numbers (see the -n option to ctags). tagurlformat A string formatting pattern used to generate links to ctags definitions. Available variables are %(path)s, %(fname)s and %(fext)s. Defaults to an empty string, resulting in just #prefix-number links. filename A string used to generate a filename when rendering <pre> blocks, for example if displaying source code. wrapcode Wrap the code inside <pre> blocks using <code>, as recommended by the HTML5 specification. """ tablestyling = "" if noclasses: tablestyling += "text-align: left;" if gc("cssclasses") and not gc("css_custom_class_per_style"): css_class = "highlight" # the default for pygments else: css_class = f"shf__{mystyle}__highlight" my_formatter = HtmlFormatter( cssclass=css_class, cssstyles=tablestyling, font_size=16, linenos=linenos, lineseparator="<br>", nobackground= False, # True would solve night mode problem without any config (as long as no line numbers are used) noclasses=noclasses, style=mystyle, wrapcode=True) except ClassNotFound as e: print(e) print(ERR_STYLE) showError(ERR_STYLE, parent=ed.parentWindow) return False pygmntd = highlight(code, my_lexer, my_formatter).rstrip() # when using noclasses/inline styling pygments adds line-height 125%, see # see https://github.com/pygments/pygments/blob/2fe2152377e317fd215776b6d7467bda3e8cda28/pygments/formatters/html.py#L269 # It's seems to be only relevant for IE and makes the line numbers misaligned on my PC. So I remove it. if noclasses: pygmntd = pygmntd.replace('line-height: 125%;', '') if inline: pretty_code = "".join([pygmntd, "<br>"]) replacements = { '<div class=': '<span class=', "<pre": "<code", "</pre></div>": "</code></span>", "<br>": "", "</br>": "", "</ br>": "", "<br />": "", 'style="line-height: 125%"': '', } for k, v in replacements.items(): pretty_code = pretty_code.replace(k, v) else: if linenos: pretty_code = "".join([pygmntd, "<br>"]) # to show line numbers pygments uses a table. The background color for the code # highlighting is limited to this table # If pygments doesn't use linenumbers it doesn't use a table. This means # that the background color covers the whole width of the card. # I don't like this. I didn't find an easier way than reusing the existing # solution. # Glutanimate removed the table in the commit # https://github.com/glutanimate/syntax-highlighting/commit/afbf5b3792611ecd2207b9975309d05de3610d45 # which hasn't been published on Ankiweb in 2019-10-02. else: pretty_code = "".join([ f'<table class="{css_class}table"><tbody><tr><td>', pygmntd, "</td></tr></tbody></table><br>" ]) """ I can't solely rely on the pygments-HTMLFormatter A lot of the stuff I did before 2020-11 with bs4 can indeed be done by adjusting the HTMLFormatter options: - I can override the ".card {text-align: center}" by using the option "cssstyles" (Inline CSS styles for the wrapping <div> tag). - I can set a custom class by adjusting the option "cssclass" which defaults to "highlight" Besides this there are the classes linenos and linenodiv. BUT I don't need to customize the latter classes. I can also work with longer css rules: /*syntax highlighting fork add-on: dark background*/ .night_mode .shf__default__highlight{ background-color: #222222 !important; } /*syntax highlighting fork add-on: line numbers: white on black: sometimes a span is used, sometimes not*/ .night_mode .shf__default__highlighttable tr td.linenos div.linenodiv pre span { background-color: #222222 !important; color: #f0f0f0 !important; } .night_mode .shf__default__highlighttable tr td.linenos div.linenodiv pre { background-color: #222222 !important; color: #f0f0f0 !important; } BUT as far as I see I can't set inline styling for the surrounding table. But to center the table I need to add something like "margin: 0 auto;". If you rely on css it's easy because the "the wrapping table will have a CSS class of [the cssclass] string plus 'table', the default is accordingly 'highlighttable'.". But my option should work without the user adjusting each template and the editor. I also need to set the font. """ if centerfragments or (noclasses and gc('font')): soup = BeautifulSoup(pretty_code, 'html.parser') if centerfragments: tablestyling = "margin: 0 auto;" for t in soup.findAll("table"): if t.has_attr('style'): t['style'] = tablestyling + t['style'] else: t['style'] = tablestyling if noclasses and gc('font'): for t in soup.findAll("code"): t['style'] = "font-family: %s;" % gc('font') pretty_code = str(soup) if noclasses: out = json.dumps(pretty_code).replace('\n', ' ').replace('\r', '') # In 2020-05 I don't remember why I used backticks/template literals # here in commit 6ea0fe8 from 2019-11. # Maybe for multi-line strings(?) but json.dumps shouldn't include newlines, because # json.dumps does "Serialize obj to a JSON formatted str using this conversion table." # for the conversion table see https://docs.python.org/3/library/json.html#py-to-json-table # which includes JSONEncoder(.. indent=None, separators=None,..) and # If indent ... None (the default) selects the most compact representation. # out = "`" + json.dumps(pretty_code)[1:-1] + "`" ed.web.eval("MyInsertHtml(%s);" % out) else: # setFormat is a thin wrapper in Anki around document.execCommand ed.web.eval("setFormat('inserthtml', %s);" % json.dumps(pretty_code)) LASTUSED = langAlias
def mediatest(self, cmd): self.postVars = dict(k=self.hkey, ) return self._dataOnly( self.req("newMediaTest", io.BytesIO(json.dumps(dict(cmd=cmd)).encode("utf8"))))