def setup(self): class MyBlockRenderer(HtmlRenderer): def block_code(self, text, lang): return '<pre class="unique-%s">%s</pre>' % (lang, text) def block_quote(self, text): return '<blockquote cite="my">\n%s</blockquote>' % (text) def block_html(self, text): return 'This is html: %s' % (text) def header(self, text, level): return '<h%d class="custom">%s</h%d>' % (level, text, level) def hrule(self): return 'HR\n' def list(self, text, flags): return 'LIST\n%s' % (text) def list_item(self, text, flags): return '[LIST ITEM:%s]\n' % (text.strip()) def footnotes(self, text): return '[FOOT: %s]' % (text) def footnote_def(self, text, num): return '[DEF: text=%s, num=%d' % (text, num) class MyTableRenderer(HtmlRenderer): def table(self, content): return '[TABLE content:%s]' % (content) def table_header(self, header): return '[HEADER: %s]\n' % (header) def table_body(self, body): return '[BODY: %s]' % (body) def table_row(self, text): return '[ROW:%s]' % text def table_cell(self, text, flag): return '<CELL>%s</CELL>' % text class MyParagraphRenderer(HtmlRenderer): def paragraph(self, text): return 'PARAGRAPH:%s\n' % text self.br = Markdown(MyBlockRenderer(), extensions=EXT_FENCED_CODE | EXT_FOOTNOTES) self.tr = Markdown(MyTableRenderer(), extensions=EXT_FENCED_CODE | EXT_TABLES) self.pr = Markdown(MyParagraphRenderer(), extensions=EXT_FENCED_CODE)
def init_template(self): # Markdown renderer self.rndr = HtmlRenderer() self.md = Markdown(self.rndr) # Jinja templates self.jt = template = env.get_template("header.html") self.update_buffer()
def load_posts(self): queue = [] for root, dirs, files in os.walk(self.defaults['posts']): for f in files: if f.endswith('.md'): queue.append(os.path.join(root, f)) # process for p in queue: fm = frontmatter.load(p) # check if post or standalone standalone = bool(fm.get('standalone', False)) if standalone: post = dict( title=fm.get('title', 'Untitled'), slug=slugify(unicode(fm.get('slug') or fm.get('title'))), template=fm.get('template'), is_standalone=True, content=Markdown(HtmlRenderer()).render(fm.content)) self.standalones.append(( p, post, )) continue # is post, fill out meta data post = dict( meta=dict(category=fm.get('category', 'none')), title=fm.get('title', 'Untitled'), slug=slugify(unicode(fm.get('slug') or fm.get('title'))), author=fm.get('author', self.site.get('site_author', 'Anonymous')), is_standalone=False, content=Markdown(HtmlRenderer()).render(fm.content)) if 'date' in fm.keys(): post['meta']['date'] = dateutil.parser.parse(fm['date']) else: post['meta']['date'] = datetime.now() post['link'] = os.path.join('posts', post['slug'] + '.html') self.posts.append(( p, post, ))
def setup(self): class MySpanRenderer(HtmlRenderer): def autolink(self, link, type): return '[AUTOLINK] link=%s, type=%d' % (link, type) def codespan(self, text): return '[CODESPAN] %s' % text def double_emphasis(self, text): return '[DOUBLE EMPHASIS] %s' % text def emphasis(self, text): return '[EMPHASIS] %s' % text def underline(self, text): return '[UNDERLINE] %s' % text def highlight(self, text): return '[HIGHLIGHT] %s' % text def quote(self, text): return '[QUOTE] %s' % text def image(self, link, title, alt): return '[IMG] link=%s, title=%s, alt=%s' % (link, title, alt) def linebreak(self): return '[LB]' def link(self, content, link, title): return 'link=%s, title=%s, cont=%s' % (link, title, content) def raw_html(self, text): return '[RAWHTML]%s' % text def triple_emphasis(self, text): return '[STRONG] %s' % text def strikethrough(self, text): return '[DEL] %s' % text def superscript(self, text): return '[SUP] %s' % text def footnote_ref(self, num): return '[FOOTNOTE_REF] num=%d' % num self.sr = Markdown(MySpanRenderer(), extensions=EXT_AUTOLINK | EXT_UNDERLINE | EXT_HIGHLIGHT | EXT_QUOTE | EXT_STRIKETHROUGH | EXT_SUPERSCRIPT | EXT_FOOTNOTES)
class EditWindow(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title="") self.set_border_width(5) self.set_default_size(800, 600) # Vertical box. Contains menu and PaneView self.vbox = Gtk.VBox(False, 2) self.add(self.vbox) self.init_menu() # Markdown Editor self.tv1 = GtkSource.View.new() self.tv1.set_left_margin(5) self.tv1.set_right_margin(5) self.tv1.set_name("markdownContent") self.tv1.set_show_line_numbers(True) self.tv1.set_show_line_marks(True) self.tv1.set_insert_spaces_instead_of_tabs(True) self.tv1.set_right_margin_position(80) self.tv1.set_tab_width(4) self.tv1.set_auto_indent(True) # self.tv1.set_highlight_current_line(True) #FIXME: Ugly color # Textbuffer self.buffer = GtkSource.Buffer() self.buffer.connect("changed", self.on_button_clicked) self.buffer.set_highlight_syntax(True) # Set textview buffer self.tv1.set_buffer(self.buffer) # Dunno lm = GtkSource.LanguageManager.get_default() language = lm.get_language("markdown") self.buffer.set_language(language) self.tv1.connect("key-press-event", self.on_key_press) # WebKit self.wv = WebKit.WebView() self.wv.connect("navigation-policy-decision-requested", self.on_navigation) # Scrolled Window 1 (for markdown) sw1 = Gtk.ScrolledWindow() sw1.set_hexpand(False) sw1.set_vexpand(True) # Scrolled Window 2 (for webkit) sw2 = Gtk.ScrolledWindow() sw2.set_hexpand(False) sw2.set_vexpand(True) # Add textview and webkit sw1.add(self.tv1) sw2.add(self.wv) # PaneView, contains markdown editor and html view (webkit) hpaned = Gtk.HPaned() hpaned.pack1(sw1, True, True) hpaned.pack2(sw2, True, True) self.vbox.pack_start(hpaned, True, True, 0) # Init Jinja, markdown self.init_template() # Load editor gtk styles self.load_styles() # Set windows title self.set_win_title() self.current_filepath = None def load_file_dialog(self, widget): dialog = Gtk.FileChooserDialog( "Open file", self, Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK), ) self.add_filters(dialog) response = dialog.run() if response == Gtk.ResponseType.OK: self.load_file(dialog.get_filename()) elif response == Gtk.ResponseType.CANCEL: pass # TODO? User cancelled dialog.destroy() def load_file(self, file_path=None): self.current_filename = "untitled" self.current_filepath = file_path if self.current_filepath: f = open(self.current_filepath, "r") self.buffer.set_text(f.read()) f.close() self.current_filename = file_path.split("/")[-1] self.set_win_title(self.current_filename) self.update_buffer() def save_current_file(self, widget): fp = self.current_filepath if fp: f = open(fp, "w") f.write(self.get_buffer()) f.close() def set_win_title(self, cztitle=None): title = "Markdown Editor" if cztitle: title = "{0} - {1}".format(title, cztitle) self.set_title(title) def init_menu(self): self.mb = Gtk.MenuBar() filemenu = Gtk.Menu() filem = Gtk.MenuItem("File") filem.set_submenu(filemenu) exit = Gtk.MenuItem("Exit") exit.connect("activate", Gtk.main_quit) save_as = Gtk.MenuItem("Save As...") save_as.connect("activate", self.save_as_dialog) load = Gtk.MenuItem("Open file...") load.connect("activate", self.load_file_dialog) action_save = Gtk.MenuItem("Save") action_save.connect("activate", self.save_current_file) filemenu.append(action_save) filemenu.append(load) filemenu.append(save_as) filemenu.append(exit) self.mb.append(filem) self.vbox.pack_start(self.mb, False, False, 0) def save_as_dialog(self, widget): dialog = Gtk.FileChooserDialog( "Save file", self, Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK), ) self.add_filters(dialog) response = dialog.run() if response == Gtk.ResponseType.OK: fname = dialog.get_filename() f = open(fname, "w") f.write(self.get_buffer()) f.close() elif response == Gtk.ResponseType.CANCEL: pass # TODO? User cancelled dialog.destroy() def add_filters(self, dialog): filter_text = Gtk.FileFilter() filter_markdown = Gtk.FileFilter() filter_markdown.set_name("Markdown ") filter_markdown.add_mime_type("text/x-markdown") dialog.add_filter(filter_markdown) filter_text.set_name("Plain text") filter_text.add_mime_type("text/plain") dialog.add_filter(filter_text) filter_any = Gtk.FileFilter() filter_any.set_name("Any files") filter_any.add_pattern("*") dialog.add_filter(filter_any) def init_template(self): # Markdown renderer self.rndr = HtmlRenderer() self.md = Markdown(self.rndr) # Jinja templates self.jt = template = env.get_template("header.html") self.update_buffer() def load_styles(self): self.style_provider = Gtk.CssProvider() css = open("themes/gtk.css", "rb") css_data = css.read() css.close() self.style_provider.load_from_data(css_data) Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), self.style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) def on_key_press(self, widget, event): self.update_buffer() def on_button_clicked(self, widget): self.update_buffer() def get_buffer(self): contentBuffer = self.buffer text = contentBuffer.get_text(contentBuffer.get_start_iter(), contentBuffer.get_end_iter(), False).decode( "utf-8" ) return text def update_buffer(self): # Fetch Text from buffer text = self.get_buffer() # Convert markdown text into html html_content = self.md.render(text) # Render template using Jinja rendered = self.jt.render(content=html_content) # Load page to WebView self.wv.load_string(rendered, "text/html", "utf-8", "/") def on_navigation(self, web_view, frame, request, nav_action, policy_decision, data=None): if request.get_uri() != "/": policy_decision.ignore()
class MarkdownSpanCustomRendererTest(TestCase): name = 'Markdown Span Custom Renderer' def setup(self): class MySpanRenderer(HtmlRenderer): def autolink(self, link, type): return '[AUTOLINK] link=%s, type=%d' % (link, type) def codespan(self, text): return '[CODESPAN] %s' % text def double_emphasis(self, text): return '[DOUBLE EMPHASIS] %s' % text def emphasis(self, text): return '[EMPHASIS] %s' % text def underline(self, text): return '[UNDERLINE] %s' % text def highlight(self, text): return '[HIGHLIGHT] %s' % text def quote(self, text): return '[QUOTE] %s' % text def image(self, link, title, alt): return '[IMG] link=%s, title=%s, alt=%s' % (link, title, alt) def linebreak(self): return '[LB]' def link(self, content, link, title): return 'link=%s, title=%s, cont=%s' % (link, title, content) def raw_html(self, text): return '[RAWHTML]%s' % text def triple_emphasis(self, text): return '[STRONG] %s' % text def strikethrough(self, text): return '[DEL] %s' % text def superscript(self, text): return '[SUP] %s' % text def footnote_ref(self, num): return '[FOOTNOTE_REF] num=%d' % num self.sr = Markdown(MySpanRenderer(), extensions=EXT_AUTOLINK | EXT_UNDERLINE | EXT_HIGHLIGHT | EXT_QUOTE | EXT_STRIKETHROUGH | EXT_SUPERSCRIPT | EXT_FOOTNOTES) def test_autolink(self): text = self.sr.render('<https://github.com/>') ok(text).contains('[AUTOLINK] link=https://github.com/, type=0') def test_codespan(self): text = self.sr.render('code `print 1`') ok(text).contains('code [CODESPAN] print 1') def test_emphasis(self): text = self.sr.render('*emphasis*') ok(text).contains('[EMPHASIS] emphasis') def test_double_emphasis(self): text = self.sr.render('**emphasis**') ok(text).contains('[DOUBLE EMPHASIS] emphasis') def test_underline(self): text = self.sr.render('_line_') ok(text).contains('[UNDERLINE] line') def test_highlight(self): text = self.sr.render('==line==') ok(text).contains('[HIGHLIGHT] line') def test_quote(self): text = self.sr.render('"spanquote"') ok(text).contains('[QUOTE] spanquote') def test_image(self): text = self.sr.render('![alt-string](path)') ok(text).contains('[IMG] link=path, title=None, alt=alt-string') def test_linebreak(self): text = self.sr.render('test \ntest\n') ok(text).contains('test[LB]test') def test_link(self): text = self.sr.render('[span link](https://github.com/ "github")') ok(text).contains('link=https://github.com/, title=github, cont=span link') def test_raw_html(self): text = self.sr.render('<raw>raw_html</raw>') ok(text).contains('[RAWHTML]<raw>raw_html[RAWHTML]</raw>') def test_triple_emphasis(self): text = self.sr.render('***triple emphasis***') ok(text).contains('[STRONG] triple emphasis') def test_strikethrough(self): text = self.sr.render('~~strikethrough~~') ok(text).contains('[DEL] strikethrough') def test_superscript(self): text = self.sr.render('^(superscript)') ok(text).contains('[SUP] superscript') def test_footnote_ref(self): text = self.sr.render('line1 [^1]\n\n [^1]: test1\n test2\n') ok(text).contains('[FOOTNOTE_REF] num=1')
class MarkdownBlockCustomRendererTest(TestCase): name = 'Markdown Block Custom Renderer' def setup(self): class MyBlockRenderer(HtmlRenderer): def block_code(self, text, lang): return '<pre class="unique-%s">%s</pre>' % (lang, text) def block_quote(self, text): return '<blockquote cite="my">\n%s</blockquote>' % (text) def block_html(self, text): return 'This is html: %s' % (text) def header(self, text, level): return '<h%d class="custom">%s</h%d>' % (level, text, level) def hrule(self): return 'HR\n' def list(self, text, flags): return 'LIST\n%s' % (text) def list_item(self, text, flags): return '[LIST ITEM:%s]\n' % (text.strip()) def footnotes(self, text): return '[FOOT: %s]' % (text) def footnote_def(self, text, num): return '[DEF: text=%s, num=%d' % (text, num) class MyTableRenderer(HtmlRenderer): def table(self, content): return '[TABLE content:%s]' % (content) def table_header(self, header): return '[HEADER: %s]\n' % (header) def table_body(self, body): return '[BODY: %s]' % (body) def table_row(self, text): return '[ROW:%s]' % text def table_cell(self, text, flag): return '<CELL>%s</CELL>' % text class MyParagraphRenderer(HtmlRenderer): def paragraph(self, text): return 'PARAGRAPH:%s\n' % text self.br = Markdown(MyBlockRenderer(), extensions=EXT_FENCED_CODE | EXT_FOOTNOTES) self.tr = Markdown(MyTableRenderer(), extensions=EXT_FENCED_CODE | EXT_TABLES) self.pr = Markdown(MyParagraphRenderer(), extensions=EXT_FENCED_CODE) def test_fenced_code(self): text = self.br.render('```python\ndef foo():\n pass\n```') ok(text).contains('unique-python') def test_block_quotes(self): text = self.br.render( 'A wise man once said:\n\n' ' > Isn\'t it wonderful just to be alive.\n') ok(text).diff('<p>A wise man once said:</p>\n' '<blockquote cite="my">\n' '<p>Isn't it wonderful just to be alive.</p>\n</blockquote>') def test_raw_block(self): text = self.br.render('<p>raw</p>\n') ok(text).diff('This is html: <p>raw</p>\n') def test_header(self): text = self.br.render('custom\n======\n') ok(text).diff('<h1 class="custom">custom</h1>') def test_hrule(self): text = self.br.render('* * *') ok(text).diff('HR\n') def test_list(self): text = self.br.render('* one\n* two') ok(text).diff('LIST\n[LIST ITEM:one]\n[LIST ITEM:two]\n') def test_footnotes(self): text = self.br.render('line1 [^1]\n\n [^1]: test1\n test2\n') ok(text).contains('FOOT') ok(text).contains('DEF') def test_table(self): text = self.tr.render('name | age\n-----|----\nMike | 30') ok(text).diff('[TABLE content:[HEADER: [ROW:<CELL>name</CELL><CELL>age</CELL>]]\n' '[BODY: [ROW:<CELL>Mike</CELL><CELL>30</CELL>]]]') def test_paragraph(self): text = self.pr.render('one\n\ntwo') ok(text).diff('PARAGRAPH:one\nPARAGRAPH:two\n')