diff --git a/reporting/markslider.py b/reporting/markslider.py index c1d4964..65c42fc 100755 --- a/reporting/markslider.py +++ b/reporting/markslider.py @@ -20,7 +20,7 @@ '''Markslider: a slideshow engine based on markdown.''' __author__ = "Ville Rantanen " -__version__ = "1.2.1" +__version__ = "1.3" import sys,os,argparse,re,datetime from argparse import ArgumentParser @@ -68,6 +68,16 @@ class slide_reader: #~ self.control_char_re = re.compile('[%s]' % re.escape(self.control_chars)) self.background = [] self.read() + self.pygments = False + if opts.syntax_pygments: + try: + self.pygments = Pygmentizer( + opts.syntax_formatter, + opts.syntax_style + ) + except ImportError as e: + self.pygments = False + def read(self): ''' Read a file, set pages and data ''' @@ -278,6 +288,59 @@ class slide_reader: if titles[page[0]] > 1: page[0] += " [%d/%d]"%( page_no, titles[page[0]] ) +class Pygmentizer(): + def __init__(self, formatter, style): + import pygments + import pygments.lexers + import pygments.formatters + import pygments.styles + self.pygments = pygments + self.lexers = pygments.lexers + self.formatters = pygments.formatters + self.styles = pygments.styles + + self.style = self.styles.get_style_by_name(style) + self.formatter = self.formatters.get_formatter_by_name( + formatter, + style = self.style + ) + self.lexer = None + + + def format(self, code): + in_block = False + blocks = [] + current_block = -1 + for i, row in enumerate(code): + if row.startswith("```"): + in_block = not in_block + lexer_name = row.replace("```","").strip() + if len(lexer_name) == 0: + in_block = False + if in_block: + blocks.append({ + "lexer": lexer_name, + "code": [], + "rows": [] + }) + current_block += 1 + else: + if in_block: + blocks[current_block]["code"].append(row) + blocks[current_block]["rows"].append(i) + + preformatted_rows = [] + for block in blocks: + lexer = self.lexers.get_lexer_by_name(block["lexer"]) + tokens = self.pygments.lex("\n".join(block["code"]), lexer) + formatted = self.pygments.format(tokens, self.formatter) + # ~ print(block["rows"]) + # ~ print(formatted.split("\n")) + for row, formatted_row in zip(block["rows"], formatted.split("\n")): + code[row] = formatted_row + preformatted_rows.append(row) + return code, preformatted_rows + def get_interactive_help_text(): return ''' left/right,page up/down,home,end @@ -331,6 +394,23 @@ Keyboard shortcuts: help="Disable color.") content.add_argument("--no-rename","--nr",action="store_false",dest="rename_title", default=True, help="Disable automatic renaming of duplicate titles.") + content.add_argument("--toc",action="store",dest="toc",default=False, + const="Table of Contents", type=str, nargs='?', + help="Insert table of contents. Define the header, or use default: %(const)s") + content.add_argument("--toc-page",action="store",dest="toc_page",default=2, type=int, + help="Insert table of contents on a chosen page. default: %(const)s") + content.add_argument("--toc-depth",action="store",dest="toc_depth",default=2, type=int, + choices=list(range(1,5)), + help="Table of contents display depth. default: %(const)s") + + content.add_argument("--no-pygments",action="store_false",dest="syntax_pygments",default = True, + help="Disable autocoloring syntax with Pygments. Used only for ``` code blocks, if language is mentioned, ex.: ```python . Note! Do not have empty lines after ```") + content.add_argument("--syntax-formatter",action="store",dest="syntax_formatter",default = "terminal256", + choices=['terminal', 'terminal256', 'terminal16m'], + help="Syntax highlighter formatter. default: %(default)s") + content.add_argument("--syntax-style",action="store",dest="syntax_style",default = "monokai", + help="Syntax highlighter style, see Pygments styles. default: %(default)s") + execution.add_argument("-e",action="store_true",dest="execute",default=False, help="Execute commands in `! or `> tags at show time with Enter key. WARNING: Potentially very dangerous to run others' slides with this switch!") @@ -347,14 +427,7 @@ Keyboard shortcuts: content.add_argument("-w",action="store_false",dest="wrap",default=True, help="Disable line wrapping. Cuts long lines.") - content.add_argument("--toc",action="store",dest="toc",default=False, - const="Table of Contents", type=str, nargs='?', - help="Insert table of contents. Define the header, or use default: %(const)s") - content.add_argument("--toc-page",action="store",dest="toc_page",default=2, type=int, - help="Insert table of contents on a chosen page. default: %(const)s") - content.add_argument("--toc-depth",action="store",dest="toc_depth",default=2, type=int, - choices=list(range(1,5)), - help="Table of contents display depth. default: %(const)s") + parser.add_argument("files",type=str, nargs='+', help="File(s) to show") opts=parser.parse_args() @@ -365,6 +438,7 @@ Keyboard shortcuts: opts.exit_last=True return opts + def page_print(reader,opts,offset): ''' Print a page ''' @@ -407,6 +481,22 @@ def page_print(reader,opts,offset): page = [cut_line(row,scrsize[1]-1) for row in page] parsed = md_color.parse(page) if opts.autocolor: + if reader.pygments: + + # ~ to_pygmentize = [] + # ~ pygmented_rows = [] + # ~ for token_i in range(len(parsed)): + # ~ if parsed[token_i][0] == 'multiline_code': + # ~ to_pygmentize.append(parsed[token_i][1]) + # ~ pygmented_rows.append(token_i) + # ~ if len(to_pygmentize) > 0: + preformatted, preformatted_rows = reader.pygments.format( + [x[1] for x in parsed] + ) + + for row_i in preformatted_rows: + parsed[row_i][0] = 'preformatted' + parsed[row_i][1] = preformatted[row_i] colored = md_color.colorize( parsed, not opts.color, @@ -440,6 +530,7 @@ def page_print(reader,opts,offset): sys.stdout.flush() return + def print_menu(reader,opts): bc.posprint( opts.size[0], 0, @@ -450,6 +541,7 @@ def print_menu(reader,opts): "slideshow" if opts.slideShow else ""), opts)) + def print_time(opts): now=datetime.datetime.now() bc.posprint( @@ -464,6 +556,7 @@ def print_time(opts): ) ) + def print_help(reader,opts): ''' Create a window with help message ''' helptext=get_interactive_help_text().split('\n') @@ -476,6 +569,7 @@ def print_help(reader,opts): sys.stdout.write(bc.pos(opts.size[0], opts.size[1])) inkey=getch.get() + def print_toc(reader,opts): ''' Create a window with TOC ''' text=reader.get_toc(display_position=True) @@ -506,16 +600,19 @@ def offset_change(opts,reader,offset,new_offset): offseth=min(reader.get_page_height(),new_offset[1]) return [max(0,o) for o in (offsety,offseth)] + def timeouthandler(sig,frame): #~ print(sig,frame) raise IOError("Input timeout") + def getkeypress(): try: return ord(getch.get()) except: return False + def browser(opts,files): ''' Main function for printing ''' @@ -617,10 +714,12 @@ def browser(opts,files): print(traceback.format_exc()) sys.exit(1) + def get_console_size(): rows, columns = os.popen('stty size', 'r').read().split() return (int(rows),int(columns)) + def colorify(s,opts): """ Add colors to string """ if not opts.color: @@ -628,6 +727,7 @@ def colorify(s,opts): c=bc.color_string(s)#+bc.Z return c + def cut_line(s,i): """ cut a color tagged string, and remove control chars """ s=s[:i] @@ -637,6 +737,7 @@ def cut_line(s,i): s))) return s + def add_highlight(s,opts): """ Add cursor position highlight """ if len(s.strip())==0: @@ -646,6 +747,7 @@ def add_highlight(s,opts): tagged="${Y}"+cleaned+"${Z}" return colorify(tagged,opts) + def launch(reader,opts,offset): """ Launch in a string using tags $!command$! or $>command$> Remove empty lines from beginning and end of stdout in $> commands. @@ -723,6 +825,7 @@ def launch(reader,opts,offset): return return + def modify_file(reader,offset): row=1 row_restarts=reader.file_start_page @@ -741,6 +844,7 @@ def modify_file(reader,offset): shell = True ) + def take_screenshot(reader,opts): out_file = os.path.join( opts.screenshots, @@ -758,12 +862,14 @@ def take_screenshot(reader,opts): shell = True ) + def get_open_command(): if sys.platform.startswith("darwin"): return "open" else: return "xdg-open" + def main(): global bc global getch @@ -797,5 +903,6 @@ def main(): os.path.basename(opts.screenshots), )) + if __name__ == "__main__": main() diff --git a/reporting/markslider.tar.gz b/reporting/markslider.tar.gz index 667a554..11ce773 100644 Binary files a/reporting/markslider.tar.gz and b/reporting/markslider.tar.gz differ diff --git a/reporting/md_color.py b/reporting/md_color.py index 180c14f..975fb36 100755 --- a/reporting/md_color.py +++ b/reporting/md_color.py @@ -81,32 +81,36 @@ def parse(data): block=new_block return data -def colorize(data,remove_colors=False,dark_colors=False,debug=False): + +def colorize(data, remove_colors = False, dark_colors = False, debug = False): # Start inserting colors, and printing - bc=ansi.code() - colorized=[] - cs='dc' if dark_colors else 'bc' - csc=cs+'c' - for i,line in enumerate(data): - row=line[1] - block=line[0] - multiline_block=block.startswith('multiline') + bc = ansi.code() + colorized = [] + cs = 'dc' if dark_colors else 'bc' + csc = cs + 'c' + for i, line in enumerate(data): + row = line[1] + block = line[0] + multiline_block = block.startswith('multiline') if multiline_block: - row=block_match[block][csc]+row + row = block_match[block][csc] + row if block_match[block][cs]: - row=block_match[block]['re'].sub(block_match[block][cs],row) + row = block_match[block]['re'].sub(block_match[block][cs], row) # No coloring inside block_code, nor multiline - if not (multiline_block or block=='block_code'): + if not (multiline_block or block == 'block_code'): for match in inlines: if inline_match[match]['re'].search(row): - row=inline_match[match]['re'].sub(inline_match[match][cs]+block_match[block][csc],row) + row = inline_match[match]['re'].sub( + inline_match[match][cs] + block_match[block][csc], + row + ) if remove_colors: - colored=bc.nocolor_string(row) + colored = bc.nocolor_string(row) else: - colored=bc.color_string(row) + colored = bc.color_string(row) if debug: - multistr="*" if multiline_block else " " - colored="{:<18}{:}:".format(data[i][0],multistr)+colored + multistr = "*" if multiline_block else " " + colored = "{:<18}{:}:".format(data[i][0], multistr) + colored colorized.append(colored) return colorized @@ -202,6 +206,9 @@ block_match_str={ 'empty': {'re':'(^$)', 'bc':'${Z}\\1','bcc':'${Z}', 'dc':'${Z}\\1','dcc':'${Z}'}, + 'preformatted': {'re': 'a^', # Never matches anything + 'bc':'','bcc':'', + 'dc':'','dcc':''}, } block_match=md_re_compile(block_match_str) blocks=['block_quote', 'multiline_code','hrule', 'heading','lheading','list_bullet', 'block_code', 'text', 'empty']