From 52db6b9bf42b4d5f8569a0e430f30a6222cec55b Mon Sep 17 00:00:00 2001 From: Ville Rantanen Date: Mon, 12 Oct 2020 11:37:33 +0300 Subject: [PATCH] python3inizing scripts --- reporting/ansicodes.py | 212 ++++--- reporting/ascii-codes.py | 66 +-- reporting/file_usage_by_name.py | 80 +-- reporting/markslider.py | 985 ++++++++++++++++++-------------- reporting/markslider.tar.gz | Bin 12906 -> 12970 bytes reporting/md_color.py | 441 +++++++++----- reporting/src2ans | 80 ++- reporting/srcframes2ans | 398 +++++++------ 8 files changed, 1328 insertions(+), 934 deletions(-) diff --git a/reporting/ansicodes.py b/reporting/ansicodes.py index baf47dc..7dbfebe 100644 --- a/reporting/ansicodes.py +++ b/reporting/ansicodes.py @@ -1,104 +1,156 @@ # Python library for ansi colorization -import re,sys +import re, sys + class code: - K="\033[1;30m" - R="\033[1;31m" - G="\033[1;32m" - B="\033[1;34m" - Y="\033[1;33m" - M="\033[1;35m" - C="\033[1;36m" - W="\033[1;37m" + K = "\033[1;30m" + R = "\033[1;31m" + G = "\033[1;32m" + B = "\033[1;34m" + Y = "\033[1;33m" + M = "\033[1;35m" + C = "\033[1;36m" + W = "\033[1;37m" - k="\033[0;30m" - r="\033[0;31m" - g="\033[0;32m" - b="\033[0;34m" - y="\033[0;33m" - m="\033[0;35m" - c="\033[0;36m" - w="\033[0;37m" + k = "\033[0;30m" + r = "\033[0;31m" + g = "\033[0;32m" + b = "\033[0;34m" + y = "\033[0;33m" + m = "\033[0;35m" + c = "\033[0;36m" + w = "\033[0;37m" - bk="\033[40m" - br="\033[41m" - bg="\033[42m" - by="\033[43m" - bb="\033[44m" - bm="\033[45m" - bc="\033[46m" - bw="\033[47m" + bk = "\033[40m" + br = "\033[41m" + bg = "\033[42m" + by = "\033[43m" + bb = "\033[44m" + bm = "\033[45m" + bc = "\033[46m" + bw = "\033[47m" - S = '\033[1m'#strong - s = '\033[2m'#strong off - U = '\033[4m'#underline - u = '\033[24m'#underline off - Z = '\033[0m'#zero colors - ic = '\033[7m'#inverse colors - io = '\033[27m'#inverse off - st = '\033[9m'#strike on - so = '\033[29m'#strike off - CLR = '\033[2J' - CLREND = '\033[K' - CLRBEG = '\033[1K' - CLRSCR = CLR+"\033[0;0H" + S = "\033[1m" # strong + s = "\033[2m" # strong off + U = "\033[4m" # underline + u = "\033[24m" # underline off + Z = "\033[0m" # zero colors + ic = "\033[7m" # inverse colors + io = "\033[27m" # inverse off + st = "\033[9m" # strike on + so = "\033[29m" # strike off + CLR = "\033[2J" + CLREND = "\033[K" + CLRBEG = "\033[1K" + CLRSCR = CLR + "\033[0;0H" - color_keys="K,R,G,B,Y,M,C,W,k,r,g,b,y,m,c,w,S,s,U,u,Z,ic,io,st,so,bk,br,bg,by,bb,bm,bc,bw,CLR,CLREND,CLRBEG,CLRSCR".split(",") - color_list=[K,R,G,B,Y,M,C,W,k,r,g,b,y,m,c,w,S,s,U,u,Z,ic,io,st,so,bk,br,bg,by,bb,bm,bc,bw,CLR,CLREND,CLRBEG,CLRSCR] - custom_match=re.compile(r'(\${)([0-9;]*[ABCDEFGHJKSTfminsu]+)(})') + color_keys = "K,R,G,B,Y,M,C,W,k,r,g,b,y,m,c,w,S,s,U,u,Z,ic,io,st,so,bk,br,bg,by,bb,bm,bc,bw,CLR,CLREND,CLRBEG,CLRSCR".split( + "," + ) + color_list = [ + K, + R, + G, + B, + Y, + M, + C, + W, + k, + r, + g, + b, + y, + m, + c, + w, + S, + s, + U, + u, + Z, + ic, + io, + st, + so, + bk, + br, + bg, + by, + bb, + bm, + bc, + bw, + CLR, + CLREND, + CLRBEG, + CLRSCR, + ] + custom_match = re.compile(r"(\${)([0-9;]*[ABCDEFGHJKSTfminsu]+)(})") - def pos(self,y,x): + def pos(self, y, x): """ Go to absolute position """ - return "\033["+str(y)+";"+str(x)+"H" - def column(self,x): - """ Go to absolute column """ - return "\033["+str(x)+"G" + return "\033[" + str(y) + ";" + str(x) + "H" - def posprint(self, y,x,s): + def column(self, x): + """ Go to absolute column """ + return "\033[" + str(x) + "G" + + def posprint(self, y, x, s): """ Print string at a location """ - sys.stdout.write( self.pos(y,x) + str(s) ) + sys.stdout.write(self.pos(y, x) + str(s)) self.flush() def clear(self): - sys.stdout.write( self.CLRSCR+self.pos(0,0) ) - def clear_to_end(self): - sys.stdout.write( self.CLREND ) - def clear_to_beginning(self): - sys.stdout.write( self.CLRBEG ) + sys.stdout.write(self.CLRSCR + self.pos(0, 0)) - def up(self,n=1): - sys.stdout.write( "\033["+str(n)+"A" ) - def down(self,n=1): - sys.stdout.write( "\033["+str(n)+"B" ) - def right(self,n=1): - sys.stdout.write( "\033["+str(n)+"C" ) - def left(self,n=1): - sys.stdout.write( "\033["+str(n)+"D" ) - def up_line(self,n=1): - sys.stdout.write( "\033["+str(n)+"F" ) - def down_line(self,n=1): - sys.stdout.write( "\033["+str(n)+"E" ) + def clear_to_end(self): + sys.stdout.write(self.CLREND) + + def clear_to_beginning(self): + sys.stdout.write(self.CLRBEG) + + def up(self, n=1): + sys.stdout.write("\033[" + str(n) + "A") + + def down(self, n=1): + sys.stdout.write("\033[" + str(n) + "B") + + def right(self, n=1): + sys.stdout.write("\033[" + str(n) + "C") + + def left(self, n=1): + sys.stdout.write("\033[" + str(n) + "D") + + def up_line(self, n=1): + sys.stdout.write("\033[" + str(n) + "F") + + def down_line(self, n=1): + sys.stdout.write("\033[" + str(n) + "E") def save(self): """ Save cursor position """ - sys.stdout.write( "\033[s" ) + sys.stdout.write("\033[s") + def restore(self): """ Restore cursor position """ - sys.stdout.write( "\033[u" ) + sys.stdout.write("\033[u") - def color_string(self,s): - for i,c in enumerate(self.color_keys): - s=s.replace("${"+c+"}",self.color_list[i]) + def color_string(self, s): + for i, c in enumerate(self.color_keys): + s = s.replace("${" + c + "}", self.color_list[i]) return self.custom_color(s) - def nocolor_string(self,s): - for i,c in enumerate(self.color_keys): - s=s.replace("${"+c+"}","") + + def nocolor_string(self, s): + for i, c in enumerate(self.color_keys): + s = s.replace("${" + c + "}", "") return self.custom_nocolor(s) - def custom_color(self,s): - return self.custom_match.sub('\033[\\2',s) - def custom_nocolor(self,s): - return self.custom_match.sub('',s) + def custom_color(self, s): + return self.custom_match.sub("\033[\\2", s) + + def custom_nocolor(self, s): + return self.custom_match.sub("", s) def get_keys(self): return self.color_keys @@ -109,8 +161,8 @@ class code: def demo(): """ Print all control sequences """ - c=code() - unformatted="""${S}ANSI CODES + c = code() + unformatted = """${S}ANSI CODES ==========${Z} ${S}Fo${U}rm${st}at${u}ti${ic}ng${Z} ${S}==========${Z} @@ -142,4 +194,4 @@ ${S}=====================${Z} C .right() Right y;xH .pos() Absolute Position D .left() Left """ - return(c.color_string(unformatted)) + return c.color_string(unformatted) diff --git a/reporting/ascii-codes.py b/reporting/ascii-codes.py index d6da7b3..e93be67 100755 --- a/reporting/ascii-codes.py +++ b/reporting/ascii-codes.py @@ -1,72 +1,62 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys, os + sys.path.append(os.path.dirname(os.path.realpath(__file__))) import ansicodes from argparse import ArgumentParser + def setup_options(): parser = ArgumentParser() - parser.add_argument( - "-v", - default = False, - action = "store_true" - ) + parser.add_argument("-v", default=False, action="store_true") parser.add_argument( "cs", - type = str, - default = "cp437", - nargs = '?', - help = "Character set to show, ex. ascii, cp437, cp850, cp1250, latin1 ..." + type=str, + default="cp437", + nargs="?", + help="Character set to show, ex. ascii, cp437, cp850, cp1250, latin1 ...", ) - opts=parser.parse_args() + opts = parser.parse_args() return opts + def long_format(table): bc = ansicodes.code() - for x,col in enumerate(table): - for y,row in enumerate(col): + for x, col in enumerate(table): + for y, row in enumerate(col): bc.posprint( - y + 2, - 1 + x*7, - bc.color_string( - "${c}%03d: ${G}%s${Z}"%( row ) - ) + y + 2, 1 + x * 7, bc.color_string("${c}%03d: ${G}%s${Z}" % (row)) ) def short_format(table): bc = ansicodes.code() - for x,col in enumerate(table): - for y,row in enumerate(col): - bc.posprint( - x + 3, - y * 2 + 1, - bc.color_string( - "${G}%s${Z}"%( row[1], ) - ) - ) - print('') + for x, col in enumerate(table): + for y, row in enumerate(col): + bc.posprint(x + 3, y * 2 + 1, bc.color_string("${G}%s${Z}" % (row[1],))) + print("") if __name__ == "__main__": opts = setup_options() - table=[[] for x in range(8)] - for c in range(0,256): - col = c/32 - table[col].append((c,chr(c).decode(opts.cs,'ignore').encode('utf-8','ignore'))) - #sys.stdout.write(chr(c).decode('latin1')) + table = [[] for x in range(8)] + for c in range(0, 256): + col = int(c / 32) + table[col].append( + (c, chr(c)) + ) + # sys.stdout.write(chr(c).decode('latin1')) # remove control chars table.pop(0) bc = ansicodes.code() bc.clear() if opts.v: - lines = int(25 - len(opts.cs)/2 - 2) + lines = int(25 - len(opts.cs) / 2 - 2) else: - lines = int(32 - len(opts.cs)/2 - 2) - print(bc.color_string("="*lines + " ${Y}%s${Z} "%( opts.cs, ) + "="*lines)) + lines = int(32 - len(opts.cs) / 2 - 2) + print(bc.color_string("=" * lines + " ${Y}%s${Z} " % (opts.cs,) + "=" * lines)) if opts.v: long_format(table) else: short_format(table) - print('') - + print("") diff --git a/reporting/file_usage_by_name.py b/reporting/file_usage_by_name.py index 94d16f1..8ad9463 100755 --- a/reporting/file_usage_by_name.py +++ b/reporting/file_usage_by_name.py @@ -1,69 +1,75 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys import os import pwd -from argparse import ArgumentParser +from argparse import ArgumentParser + +MINSIZE = 0 -MINSIZE=0 def setup_options(): - parser=ArgumentParser(description="List file usage") - parser.add_argument('startpath', action="append", nargs='+') + parser = ArgumentParser(description="List file usage") + parser.add_argument("startpath", action="append", nargs="+") - options=parser.parse_args() + options = parser.parse_args() return options -def add_recurse(db,options,start): - for path,dirs,files in os.walk(start,followlinks=False): - files=clean_syms(files,path) + +def add_recurse(db, options, start): + for path, dirs, files in os.walk(start, followlinks=False): + files = clean_syms(files, path) for f in files: - filename=os.path.join(path,f) - stats=os.stat(filename) - owner=stats.st_uid - size=stats.st_size + filename = os.path.join(path, f) + stats = os.stat(filename) + owner = stats.st_uid + size = stats.st_size if not owner in db: - db[owner]=0 - db[owner]+=size + db[owner] = 0 + db[owner] += size return db -def clean_syms(files,path): - nonsyms=[] + +def clean_syms(files, path): + nonsyms = [] for f in files: - if not os.path.islink(os.path.join(path,f)): + if not os.path.islink(os.path.join(path, f)): nonsyms.append(f) return nonsyms + def print_db(db): - namedb=dict() + namedb = dict() for u in db: try: - namedb[pwd.getpwuid(u).pw_name]=humanize_size(db[u]) + namedb[pwd.getpwuid(u).pw_name] = humanize_size(db[u]) except: - namedb[str(u)]=humanize_size(db[u]) - names=namedb.keys() + namedb[str(u)] = humanize_size(db[u]) + names = list(namedb.keys()) names.sort() - for n in names: - print(n+": "+namedb[n]) + for n in names: + print(n + ": " + namedb[n]) -def humanize_size(size,precision=1): - if size==None: - return 'nan' - suffixes=['B','KB','MB','GB','TB'] + +def humanize_size(size, precision=1): + if size == None: + return "nan" + suffixes = ["B", "KB", "MB", "GB", "TB"] suffixIndex = 0 - defPrecision=0 + defPrecision = 0 while size > 1024: - suffixIndex += 1 #increment the index of the suffix - size = size/1024.0 #apply the division - defPrecision=precision - return "%.*f%s"%(defPrecision,size,suffixes[suffixIndex]) + suffixIndex += 1 # increment the index of the suffix + size = size / 1024.0 # apply the division + defPrecision = precision + return "%.*f%s" % (defPrecision, size, suffixes[suffixIndex]) + def main(): - options=setup_options(); - db=dict() + options = setup_options() + db = dict() for f in options.startpath[0]: - db=add_recurse(db,options,f) + db = add_recurse(db, options, f) print_db(db) sys.exit(0) -main() +main() diff --git a/reporting/markslider.py b/reporting/markslider.py index 65c42fc..c0e4a51 100755 --- a/reporting/markslider.py +++ b/reporting/markslider.py @@ -17,19 +17,21 @@ # along with this program. If not, see . # -'''Markslider: a slideshow engine based on markdown.''' +"""Markslider: a slideshow engine based on markdown.""" __author__ = "Ville Rantanen " __version__ = "1.3" -import sys,os,argparse,re,datetime +import sys, os, argparse, re, datetime from argparse import ArgumentParser -import traceback,tty,termios,subprocess,signal +import traceback, tty, termios, subprocess, signal + sys.path.append(os.path.dirname(os.path.realpath(__file__))) import ansicodes, md_color -HL=">" -EOS="# End of Slides" +HL = ">" +EOS = "# End of Slides" + class getch: def get(self): @@ -43,12 +45,15 @@ class getch: return ch -class EndProgram( Exception ): - ''' Nice exit ''' +class EndProgram(Exception): + """ Nice exit """ + pass + class slide_reader: """ Class for reading files. """ + def __init__(self, files, opts): self.filename = files[0] self.files = files @@ -64,23 +69,19 @@ class slide_reader: self.data = [] self.re_image_convert = re.compile("(.*)(!\[.*\])\((.*)\)>") self.re_command = re.compile("(.*)`(.*)`>(.*)") - #~ self.control_chars = ''.join(map(unichr, range(0,32) + range(127,160))) - #~ self.control_char_re = re.compile('[%s]' % re.escape(self.control_chars)) + # ~ self.control_chars = ''.join(map(unichr, range(0,32) + range(127,160))) + # ~ 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 - + 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 ''' + """ Read a file, set pages and data """ self.pages = 0 self.background = [] @@ -89,22 +90,22 @@ class slide_reader: first_slide_found = False for fname in self.files: first_slide_found = False - f=open(fname,'r') - new_page=[] + f = open(fname, "r") + new_page = [] in_code = False for row in f: if not row: continue - row=row.rstrip("\n\r ") + row = row.rstrip("\n\r ") # find end of show if row == EOS: break # find header to start a new page if row.startswith("#") and not row.startswith("##") and not in_code: - first_slide_found=True - if len(new_page)>0: + first_slide_found = True + if len(new_page) > 0: self.data.append(new_page) - new_page=[] + new_page = [] if row.startswith("```"): in_code = not in_code # if first slide havent been found yet: @@ -112,30 +113,32 @@ class slide_reader: self.background.append(row) continue new_page.extend(self.generate_content(row)) - if len(new_page)>0: + if len(new_page) > 0: self.data.append(new_page) f.close() self.file_start_page.append(len(self.data)) - if len(self.data)==0: + if len(self.data) == 0: raise ValueError("File does not have a # header") self.rename_duplicates() self.toc() - self.pages=len(self.data) + self.pages = len(self.data) self.inc_page_no(0) - self.max_width=0 - self.max_height=0 + self.max_width = 0 + self.max_height = 0 for page in self.data: - self.max_height=max(self.max_height, len(page)) + self.max_height = max(self.max_height, len(page)) for row in page: - self.max_width=max(self.max_width, len(row)) + self.max_width = max(self.max_width, len(row)) def get_data(self): return self.data + def get_current_filename(self): - for i,offset in enumerate(self.file_start_page): - if offset>self.page: + for i, offset in enumerate(self.file_start_page): + if offset > self.page: return self.files[i] return "NA" + def get_filename(self): return self.filename @@ -153,121 +156,136 @@ class slide_reader: def get_page_no(self): return self.page - def inc_page_no(self,inc=1,loop=False): - self.page+=inc - if self.page<0: - self.page=0 + + def inc_page_no(self, inc=1, loop=False): + self.page += inc + if self.page < 0: + self.page = 0 if loop: - self.page=self.pages-1 - if self.page>=self.pages: - self.page=self.pages-1 + self.page = self.pages - 1 + if self.page >= self.pages: + self.page = self.pages - 1 if loop: - self.page=0 - self.width=max([len(x) for x in self.data[self.page]]) - self.height=len(self.data[self.page]) + self.page = 0 + self.width = max([len(x) for x in self.data[self.page]]) + self.height = len(self.data[self.page]) + def last_page(self): - self.page=self.pages-1 + self.page = self.pages - 1 + def first_page(self): - self.page=0 + self.page = 0 + def get_page_height(self): return self.height + def get_page_width(self): return self.width + def get_max_height(self): return self.max_height + def get_max_width(self): return self.max_width - def get_toc(self,display_position=False): - title=self.opts.toc if self.opts.toc else "Table of Contents" - TOC=["# "+title,""] - offset=(self.opts.toc_page-1) if self.opts.toc else 0 - for h1,page in enumerate(self.data[offset:]): - title=page[0].strip("# ") - if display_position and h1==self.page-offset: - title="_%s_"%(title,) - TOC.append("%d. %s"%(h1+1,title)) - subh=[0,0,0] - if self.opts.toc_depth>1: + def get_toc(self, display_position=False): + title = self.opts.toc if self.opts.toc else "Table of Contents" + TOC = ["# " + title, ""] + offset = (self.opts.toc_page - 1) if self.opts.toc else 0 + for h1, page in enumerate(self.data[offset:]): + title = page[0].strip("# ") + if display_position and h1 == self.page - offset: + title = "_%s_" % (title,) + TOC.append("%d. %s" % (h1 + 1, title)) + subh = [0, 0, 0] + if self.opts.toc_depth > 1: for line in page: - title=line.strip("# ") + title = line.strip("# ") if re.search("^##[^#]", line): - subh=[ subh[0]+1, 0, 0 ] - TOC.append(" %d.%d. %s"%(h1+1,subh[0],title)) - if self.opts.toc_depth==2: continue + subh = [subh[0] + 1, 0, 0] + TOC.append(" %d.%d. %s" % (h1 + 1, subh[0], title)) + if self.opts.toc_depth == 2: + continue if re.search("^###[^#]", line): - subh=[ subh[0], subh[1]+1, 0 ] - TOC.append(" %d.%d.%d. %s"%(h1+1,subh[0],subh[1],title)) - if self.opts.toc_depth==3: continue + subh = [subh[0], subh[1] + 1, 0] + TOC.append( + " %d.%d.%d. %s" % (h1 + 1, subh[0], subh[1], title) + ) + if self.opts.toc_depth == 3: + continue if re.search("^####[^#]", line): - subh=[ subh[0], subh[1], subh[2]+1 ] - TOC.append(" %d.%d.%d.%d. %s"%(h1+1,subh[0],subh[1],subh[2],title)) + subh = [subh[0], subh[1], subh[2] + 1] + TOC.append( + " %d.%d.%d.%d. %s" + % (h1 + 1, subh[0], subh[1], subh[2], title) + ) return TOC def toc(self): if self.opts.toc: - TOC=self.get_toc() - self.data.insert(self.opts.toc_page-1,TOC) + TOC = self.get_toc() + self.data.insert(self.opts.toc_page - 1, TOC) # adding 1 is not fullproof, if toc page is after the first document - self.file_start_page=[1+i for i in self.file_start_page] + self.file_start_page = [1 + i for i in self.file_start_page] - def generate_content(self,s): + def generate_content(self, s): """ Check for launchable items, or converted images """ if self.opts.execute_read: - if s.find("`>")>-1: - command=self.re_command.match(s) - if command !=None: + if s.find("`>") > -1: + command = self.re_command.match(s) + if command != None: return self.launch(command) image = self.re_image_convert.match(s) if image != None: return self.convert_image(image) return [s] - def launch(self,command): - """ Launch in a string using tags `command`> - Remove empty lines from beginning and end of stdout. + def launch(self, command): + """Launch in a string using tags `command`> + Remove empty lines from beginning and end of stdout. """ - output = subprocess.check_output( - command.group(2).strip(), - shell = True - ) + output = subprocess.check_output(command.group(2).strip(), shell=True) if type(output) == bytes: - output = output.decode('utf-8') + output = output.decode("utf-8") output = output.split("\n") - while len(output[0].strip())==0: - if len(output)==1: return [""] + while len(output[0].strip()) == 0: + if len(output) == 1: + return [""] del output[0] - while len(output[-1].strip())==0: - if len(output)==1: return [""] + while len(output[-1].strip()) == 0: + if len(output) == 1: + return [""] del output[-1] - return_value=[command.group(1)] + return_value = [command.group(1)] return_value.extend(output) return_value.append(command.group(3)) return return_value # return [s] - def convert_image(self,image): - """ comnvert image using tags ![]()> - Remove empty lines from beginning and end of stdout. + def convert_image(self, image): + """comnvert image using tags ![]()> + Remove empty lines from beginning and end of stdout. """ - #~ 2=title - #~ 3=image command + # ~ 2=title + # ~ 3=image command output = subprocess.check_output( - "convert %s JPEG:- | jp2a --colors --width=70 -"%( image.group(3),), - shell=True + "convert %s JPEG:- | jp2a --colors --width=70 -" % (image.group(3),), + shell=True, ) if type(output) == bytes: - output = output.decode('utf-8') + output = output.decode("utf-8") output = output.split("\n") - while len(output[0].strip())==0: - if len(output)==1: return [""] + while len(output[0].strip()) == 0: + if len(output) == 1: + return [""] del output[0] - while len(output[-1].strip())==0: - if len(output)==1: return [""] + while len(output[-1].strip()) == 0: + if len(output) == 1: + return [""] del output[-1] - return_value=[image.group(1)] + return_value = [image.group(1)] return_value.extend(output) - #~ return_value.append(image.group(4)) + # ~ return_value.append(image.group(4)) return return_value # return [s] @@ -286,64 +304,60 @@ class slide_reader: for page, page_no in zip(self.data, page_nos): 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 + page[0] += " [%d/%d]" % (page_no, titles[page[0]]) - 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) +class Pygmentizer: + def __init__(self, formatter, style): + import pygments + import pygments.lexers + import pygments.formatters + import pygments.styles - 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 + 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 + return """ left/right,page up/down,home,end change page c list contents (toc) M modify file with VIM @@ -354,11 +368,13 @@ def get_interactive_help_text(): ,/. scroll page up/down move highlight enter execute highlighted line - h help''' + h help""" + def setup_options(): - ''' Create command line options ''' - usage=''' + """ Create command line options """ + usage = ( + """ MarkSlider: a markdown based slideshow engine Special syntaxes: * Colors: insert string ${C}, where C is one of KRGBYMCWkrgbymcwSUZ @@ -369,88 +385,192 @@ Special syntaxes: * Convert images to ASCII, with jp2a: ![](file.jpg)> Keyboard shortcuts: -'''+get_interactive_help_text() +""" + + get_interactive_help_text() + ) - parser=ArgumentParser(description=usage, - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=__author__) - parser.add_argument("-v","--version",action="version",version=__version__) - parser.add_argument("--export",action="store",dest="screenshots",default=False, - type=str, help="Take screenshots of the slideshow in the given folder.") + parser = ArgumentParser( + description=usage, + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__author__, + ) + parser.add_argument("-v", "--version", action="version", version=__version__) + parser.add_argument( + "--export", + action="store", + dest="screenshots", + default=False, + type=str, + help="Take screenshots of the slideshow in the given folder.", + ) - content = parser.add_argument_group('content') - execution = parser.add_argument_group('execution') - control = parser.add_argument_group('controls') + content = parser.add_argument_group("content") + execution = parser.add_argument_group("execution") + control = parser.add_argument_group("controls") - content.add_argument("--background",action="store_true",dest="background",default = False, - help="Use the rows before the first # header as slide background") - content.add_argument("--center",action="store_true",dest="center",default=False, - help="Center slides on screen.") - content.add_argument("--dc",action="store_true",dest="dark_colors",default=False, - help="Use dark colorscheme, better for white background terminals.") - content.add_argument("-m",action="store_false",dest="autocolor",default=True, - help="Disable color by markdown structure.") - content.add_argument("--no-color","-n",action="store_false",dest="color",default=True, - 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( + "--background", + action="store_true", + dest="background", + default=False, + help="Use the rows before the first # header as slide background", + ) + content.add_argument( + "--center", + action="store_true", + dest="center", + default=False, + help="Center slides on screen.", + ) + content.add_argument( + "--dc", + action="store_true", + dest="dark_colors", + default=False, + help="Use dark colorscheme, better for white background terminals.", + ) + content.add_argument( + "-m", + action="store_false", + dest="autocolor", + default=True, + help="Disable color by markdown structure.", + ) + content.add_argument( + "--no-color", + "-n", + action="store_false", + dest="color", + default=True, + 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") + 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!", + ) + execution.add_argument( + "-E", + action="store_true", + dest="execute_read", + default=False, + help="Execute commands in ``> tags at file read time. WARNING: Potentially very dangerous to run others' slides with this switch!", + ) - 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!") - execution.add_argument("-E",action="store_true",dest="execute_read",default=False, - help="Execute commands in ``> tags at file read time. WARNING: Potentially very dangerous to run others' slides with this switch!") + control.add_argument( + "--exit", + action="store_true", + dest="exit_last", + default=False, + help="Exit after last slide.", + ) - control.add_argument("--exit",action="store_true",dest="exit_last",default=False, - help="Exit after last slide.") + control.add_argument( + "-s", + action="store_false", + dest="menu", + default=True, + help="Disable status bar.", + ) + control.add_argument( + "--timer", + action="store", + dest="slideTimer", + default=False, + type=int, + help="Timer for slideshow. If set, starts automatic slide changing.", + ) + content.add_argument( + "-w", + action="store_false", + dest="wrap", + default=True, + help="Disable line wrapping. Cuts long lines.", + ) - control.add_argument("-s",action="store_false",dest="menu",default=True, - help="Disable status bar.") - control.add_argument("--timer",action="store",dest="slideTimer",default=False, type=int, - help="Timer for slideshow. If set, starts automatic slide changing.") - content.add_argument("-w",action="store_false",dest="wrap",default=True, - help="Disable line wrapping. Cuts long lines.") - - - parser.add_argument("files",type=str, nargs='+', - help="File(s) to show") - opts=parser.parse_args() - opts.slideShow=not not opts.slideTimer + parser.add_argument("files", type=str, nargs="+", help="File(s) to show") + opts = parser.parse_args() + opts.slideShow = not not opts.slideTimer if opts.screenshots: - opts.slideShow=True - opts.slideTimer=1 - opts.exit_last=True + opts.slideShow = True + opts.slideTimer = 1 + opts.exit_last = True return opts -def page_print(reader,opts,offset): - ''' Print a page ''' +def page_print(reader, opts, offset): + """ Print a page """ page = reader.get_current_page() scrsize = opts.size # clear page bc.clear() - if opts.center: # Placeholder for 80x25 center alignment + if opts.center: # Placeholder for 80x25 center alignment align_width = reader.get_max_width() - align_x_offset = int(scrsize[1]/2 - align_width/2) + align_x_offset = int(scrsize[1] / 2 - align_width / 2) align_pad = " " * align_x_offset - align_y_offset = int(scrsize[0]/2 - reader.get_max_height()/2) + align_y_offset = int(scrsize[0] / 2 - reader.get_max_height() / 2) bc.down_line(align_y_offset) else: align_pad = "" @@ -459,26 +579,24 @@ def page_print(reader,opts,offset): coloring = "${b}${U}" else: coloring = "${U}${Y}" - print( - align_pad + - colorify( - coloring + page[0], - opts) + - bc.Z - ) + print(align_pad + colorify(coloring + page[0], opts) + bc.Z) if opts.background: bc.save() if opts.color: - sys.stdout.write("\n".join([align_pad + bc.color_string(x) for x in reader.background])) + sys.stdout.write( + "\n".join([align_pad + bc.color_string(x) for x in reader.background]) + ) else: - sys.stdout.write("\n".join([align_pad + bc.nocolor_string(x) for x in reader.background])) + sys.stdout.write( + "\n".join([align_pad + bc.nocolor_string(x) for x in reader.background]) + ) bc.restore() - if (sys.version_info < (3, 0)): + if sys.version_info < (3, 0): # python2 magic - page = [row.decode('utf-8') for row in page] + page = [row.decode("utf-8") for row in page] # Print page rows if not opts.wrap: - page = [cut_line(row,scrsize[1]-1) for row in page] + page = [cut_line(row, scrsize[1] - 1) for row in page] parsed = md_color.parse(page) if opts.autocolor: if reader.pygments: @@ -486,41 +604,39 @@ def page_print(reader,opts,offset): # ~ 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 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] + [x[1] for x in parsed] ) for row_i in preformatted_rows: - parsed[row_i][0] = 'preformatted' + parsed[row_i][0] = "preformatted" parsed[row_i][1] = preformatted[row_i] - colored = md_color.colorize( - parsed, - not opts.color, - opts.dark_colors - ) + colored = md_color.colorize(parsed, not opts.color, opts.dark_colors) else: if opts.color: - colored=[bc.color_string(row[1]) for row in parsed] + colored = [bc.color_string(row[1]) for row in parsed] else: - colored=[bc.nocolor_string(row[1]) for row in parsed] + colored = [bc.nocolor_string(row[1]) for row in parsed] r = 0 for row_i in range(len(page)): - if row_i == 0: continue - if row_i < offset[0]: continue + if row_i == 0: + continue + if row_i < offset[0]: + continue row = page[row_i] - #page[1+offset[0]:]: + # page[1+offset[0]:]: if opts.wrap: - row_lines = int(float(len(row))/scrsize[1]) + row_lines = int(float(len(row)) / scrsize[1]) else: row_lines = 0 row_lines = 0 - colored_row = colored[row_i]#=colorify(row,opts) + colored_row = colored[row_i] # =colorify(row,opts) if offset[1] == r + 1 + offset[0]: - colored_row = add_highlight(row,opts) + colored_row = add_highlight(row, opts) sys.stdout.write(align_pad + colored_row) if r >= scrsize[0] - 2: @@ -531,78 +647,85 @@ def page_print(reader,opts,offset): return -def print_menu(reader,opts): +def print_menu(reader, opts): - bc.posprint( opts.size[0], 0, - colorify("${y}%d${Z}/%d %s|%s"%( - 1+reader.get_page_no(), - reader.get_pages(), - os.path.basename(reader.get_current_filename()), - "slideshow" if opts.slideShow else ""), - opts)) - - -def print_time(opts): - now=datetime.datetime.now() bc.posprint( opts.size[0], - opts.size[1]-5, + 0, colorify( - "%02d:%02d"%( - now.hour, - now.minute + "${y}%d${Z}/%d %s|%s" + % ( + 1 + reader.get_page_no(), + reader.get_pages(), + os.path.basename(reader.get_current_filename()), + "slideshow" if opts.slideShow else "", ), - opts - ) + opts, + ), ) -def print_help(reader,opts): - ''' Create a window with help message ''' - helptext=get_interactive_help_text().split('\n') - maxlen=max([len(x) for x in helptext]) - bc.posprint(3,5," +"+"-"*maxlen+"+ ") - bc.posprint(4,5,colorify(" |${U}${Y}"+("{:^"+str(maxlen)+"}").format("Help")+"${Z}| ",opts)) - for y,row in enumerate(helptext): - bc.posprint(5+y,5,(" |{:<"+str(maxlen)+"}| ").format(row)) - bc.posprint(6+y,5," +"+"-"*maxlen+"+ ") +def print_time(opts): + now = datetime.datetime.now() + bc.posprint( + opts.size[0], + opts.size[1] - 5, + colorify("%02d:%02d" % (now.hour, now.minute), opts), + ) + + +def print_help(reader, opts): + """ Create a window with help message """ + helptext = get_interactive_help_text().split("\n") + maxlen = max([len(x) for x in helptext]) + bc.posprint(3, 5, " +" + "-" * maxlen + "+ ") + bc.posprint( + 4, + 5, + colorify( + " |${U}${Y}" + ("{:^" + str(maxlen) + "}").format("Help") + "${Z}| ", opts + ), + ) + for y, row in enumerate(helptext): + bc.posprint(5 + y, 5, (" |{:<" + str(maxlen) + "}| ").format(row)) + bc.posprint(6 + y, 5, " +" + "-" * maxlen + "+ ") sys.stdout.write(bc.pos(opts.size[0], opts.size[1])) - inkey=getch.get() + inkey = getch.get() -def print_toc(reader,opts): - ''' Create a window with TOC ''' - text=reader.get_toc(display_position=True) - title=opts.toc if opts.toc else "Table of Contents" - maxlen=max([len(x) for x in text]) - bc.posprint(3,2," +"+"-"*maxlen+"+ ") - parsed=md_color.parse(text) +def print_toc(reader, opts): + """ Create a window with TOC """ + text = reader.get_toc(display_position=True) + title = opts.toc if opts.toc else "Table of Contents" + maxlen = max([len(x) for x in text]) + bc.posprint(3, 2, " +" + "-" * maxlen + "+ ") + parsed = md_color.parse(text) if opts.autocolor: - colored=md_color.colorize(parsed,not opts.color,opts.dark_colors) + colored = md_color.colorize(parsed, not opts.color, opts.dark_colors) else: if opts.color: - colored=[bc.color_string(row[1]) for row in parsed] + colored = [bc.color_string(row[1]) for row in parsed] else: - colored=[bc.nocolor_string(row[1]) for row in parsed] - for y,row in enumerate(colored): - bc.posprint(4+y,2,(" |{:<"+str(maxlen)+"}| ").format(" ")) - bc.posprint(4+y,3,("|{:<"+str(maxlen)+"}").format(row)) - bc.posprint(5+y,2," +"+"-"*maxlen+"+ ") + colored = [bc.nocolor_string(row[1]) for row in parsed] + for y, row in enumerate(colored): + bc.posprint(4 + y, 2, (" |{:<" + str(maxlen) + "}| ").format(" ")) + bc.posprint(4 + y, 3, ("|{:<" + str(maxlen) + "}").format(row)) + bc.posprint(5 + y, 2, " +" + "-" * maxlen + "+ ") sys.stdout.write(bc.pos(opts.size[0], opts.size[1])) - inkey=getch.get() + inkey = getch.get() -def offset_change(opts,reader,offset,new_offset): - ''' Change the display position of page ''' - new_offset=(offset[0]+new_offset[0], offset[1]+new_offset[1]) - offsety=min(reader.get_page_height()-1,new_offset[0]) - offseth=min(reader.get_page_height(),new_offset[1]) - return [max(0,o) for o in (offsety,offseth)] +def offset_change(opts, reader, offset, new_offset): + """ Change the display position of page """ + new_offset = (offset[0] + new_offset[0], offset[1] + new_offset[1]) + offsety = min(reader.get_page_height() - 1, new_offset[0]) + 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) +def timeouthandler(sig, frame): + # ~ print(sig,frame) raise IOError("Input timeout") @@ -613,28 +736,28 @@ def getkeypress(): return False -def browser(opts,files): - ''' Main function for printing ''' +def browser(opts, files): + """ Main function for printing """ try: - reader=slide_reader(files,opts) + reader = slide_reader(files, opts) except: print("Error in reading the file:") for o in sys.exc_info(): print(o) sys.exit(1) - offset=(0,0) + offset = (0, 0) try: while 1: - opts.size=get_console_size() - page_print(reader,opts,offset) + opts.size = get_console_size() + page_print(reader, opts, offset) if opts.menu: - print_menu(reader,opts) + print_menu(reader, opts) print_time(opts) sys.stdout.write(bc.pos(opts.size[0], opts.size[1])) sys.stdout.flush() if opts.screenshots: - take_screenshot(reader,opts) + take_screenshot(reader, opts) while True: if opts.slideTimer and opts.slideShow: signal.signal(signal.SIGALRM, timeouthandler) @@ -642,7 +765,7 @@ def browser(opts,files): elif opts.menu: signal.signal(signal.SIGALRM, timeouthandler) signal.alarm(15) - inkey=getkeypress() + inkey = getkeypress() signal.alarm(0) if not inkey and not opts.slideShow and opts.menu: # normal operation, just update the time @@ -651,58 +774,57 @@ def browser(opts,files): if not inkey and opts.slideShow: # slideshow mode if opts.exit_last: - if reader.page+1==reader.pages: + if reader.page + 1 == reader.pages: return - reader.inc_page_no(1,True) - offset=(0,0) + reader.inc_page_no(1, True) + offset = (0, 0) break - #~ print(inkey) - if inkey in [113,3,120]: - #print('Exited in: ') + # ~ print(inkey) + if inkey in [113, 3, 120]: + # print('Exited in: ') return - if inkey in [67,54,32]: # PGDN or space + if inkey in [67, 54, 32]: # PGDN or space if opts.exit_last: - if reader.page+1==reader.pages: + if reader.page + 1 == reader.pages: return reader.inc_page_no(1) - offset=(0, 0) - if inkey in [68,53,127]: + offset = (0, 0) + if inkey in [68, 53, 127]: reader.inc_page_no(-1) - offset=(0, 0) - if inkey==72 or inkey==49: # HOME + offset = (0, 0) + if inkey == 72 or inkey == 49: # HOME reader.first_page() - offset=(0, 0) - if inkey==70 or inkey==52: # END + offset = (0, 0) + if inkey == 70 or inkey == 52: # END reader.last_page() - offset=(0, 0) - if inkey==ord('c'): - print_toc(reader,opts) - if inkey==ord('h'): - print_help(reader,opts) - if inkey==ord('s'): - opts.menu=not opts.menu - if inkey==ord('t'): - opts.slideShow=not opts.slideShow - if inkey==ord('r'): + offset = (0, 0) + if inkey == ord("c"): + print_toc(reader, opts) + if inkey == ord("h"): + print_help(reader, opts) + if inkey == ord("s"): + opts.menu = not opts.menu + if inkey == ord("t"): + opts.slideShow = not opts.slideShow + if inkey == ord("r"): reader.read() - offset=offset_change(opts,reader,offset,(0, 0)) - if inkey==ord('M'): - modify_file(reader,offset) + offset = offset_change(opts, reader, offset, (0, 0)) + if inkey == ord("M"): + modify_file(reader, offset) reader.read() - offset=offset_change(opts,reader,offset,(0, 0)) - if inkey==ord(','): - offset=offset_change(opts,reader,offset,(-1, 0)) - if inkey==ord('.'): - offset=offset_change(opts,reader,offset,(1, 0)) - if inkey==65: # up - offset=offset_change(opts,reader,offset,(0, -1)) - if inkey==66: # down - offset=offset_change(opts,reader,offset,(0, 1)) - if inkey==13: # enter - launch(reader,opts,offset) + offset = offset_change(opts, reader, offset, (0, 0)) + if inkey == ord(","): + offset = offset_change(opts, reader, offset, (-1, 0)) + if inkey == ord("."): + offset = offset_change(opts, reader, offset, (1, 0)) + if inkey == 65: # up + offset = offset_change(opts, reader, offset, (0, -1)) + if inkey == 66: # down + offset = offset_change(opts, reader, offset, (0, 1)) + if inkey == 13: # enter + launch(reader, opts, offset) break - except IOError: pass except KeyboardInterrupt: @@ -716,150 +838,134 @@ def browser(opts,files): def get_console_size(): - rows, columns = os.popen('stty size', 'r').read().split() - return (int(rows),int(columns)) + rows, columns = os.popen("stty size", "r").read().split() + return (int(rows), int(columns)) -def colorify(s,opts): +def colorify(s, opts): """ Add colors to string """ if not opts.color: return bc.nocolor_string(s) - c=bc.color_string(s)#+bc.Z + c = bc.color_string(s) # +bc.Z return c -def cut_line(s,i): +def cut_line(s, i): """ cut a color tagged string, and remove control chars """ - s=s[:i] - s=re.sub("\$$","", - re.sub("\$\{$","", - re.sub("\$\{.$","", - s))) + s = s[:i] + s = re.sub("\$$", "", re.sub("\$\{$", "", re.sub("\$\{.$", "", s))) return s -def add_highlight(s,opts): +def add_highlight(s, opts): """ Add cursor position highlight """ - if len(s.strip())==0: - cleaned=HL + if len(s.strip()) == 0: + cleaned = HL else: - cleaned=bc.nocolor_string(s) - tagged="${Y}"+cleaned+"${Z}" - return colorify(tagged,opts) + cleaned = bc.nocolor_string(s) + 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. - Detects URLS and markdown images ![Alt text](/path/to/img.jpg) +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. + Detects URLS and markdown images ![Alt text](/path/to/img.jpg) """ if not opts.execute: - bc.posprint( - offset[1]-offset[0]+1, - 0, - "Execution not enabled!" - ) + bc.posprint(offset[1] - offset[0] + 1, 0, "Execution not enabled!") inkey = getch.get() return s = reader.get_current_page()[offset[1]] - urls = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', s) - images = re.findall('!\[[^]]+\]\([^\)]+\)', s) + urls = re.findall( + "http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+", + s, + ) + images = re.findall("!\[[^]]+\]\([^\)]+\)", s) # sanity check if s.find("`") == -1 and len(urls) == 0 and len(images) == 0: return - run_command=re.match("(.*)`(.*)`!(.*)",s) - show_command=re.match("(.*)`(.*)`>(.*)",s) + run_command = re.match("(.*)`(.*)`!(.*)", s) + show_command = re.match("(.*)`(.*)`>(.*)", s) if show_command != None: - output = subprocess.check_output( - show_command.group(2).strip(), - shell = True - ) + output = subprocess.check_output(show_command.group(2).strip(), shell=True) if type(output) == bytes: - output = output.decode('utf-8') + output = output.decode("utf-8") output = output.split("\n") - while len(output[0].strip())==0: - if len(output)==1: return + while len(output[0].strip()) == 0: + if len(output) == 1: + return del output[0] - while len(output[-1].strip())==0: - if len(output)==1: return + while len(output[-1].strip()) == 0: + if len(output) == 1: + return del output[-1] - for y,l in enumerate(output): - bc.posprint(y+offset[1]-offset[0]+2,0,' '*len(l)) + for y, l in enumerate(output): + bc.posprint(y + offset[1] - offset[0] + 2, 0, " " * len(l)) bc.clear_to_end() - bc.posprint(y+offset[1]-offset[0]+2,0,l) - inkey=getch.get() + bc.posprint(y + offset[1] - offset[0] + 2, 0, l) + inkey = getch.get() return if run_command != None: - subprocess.call( - run_command.group(2), - shell = True, - executable = "/bin/bash" - ) - inkey=getch.get() + subprocess.call(run_command.group(2), shell=True, executable="/bin/bash") + inkey = getch.get() return # Open URLS last - if len(urls)>0: + if len(urls) > 0: # Remove ) at the end of url: [name](link) markdown syntax subprocess.call( - "%s '%s' &"%( + "%s '%s' &" + % ( get_open_command(), urls[0].rstrip(")"), ), - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - shell = True + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True, ) return - if len(images)>0: - image = re.sub('.*\(([^\)]+)\).*', "\\1",images[0]) + if len(images) > 0: + image = re.sub(".*\(([^\)]+)\).*", "\\1", images[0]) subprocess.call( - "%s '%s' &"%( + "%s '%s' &" + % ( get_open_command(), image, ), - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - shell = True + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True, ) return return -def modify_file(reader,offset): - row=1 - row_restarts=reader.file_start_page +def modify_file(reader, offset): + row = 1 + row_restarts = reader.file_start_page for page in range(reader.page): - if opts.toc_page==page+1 and opts.toc: + if opts.toc_page == page + 1 and opts.toc: continue - row+=len(reader.data[page]) - if (page+1) in row_restarts: - row=1 + row += len(reader.data[page]) + if (page + 1) in row_restarts: + row = 1 subprocess.call( - "vim +%d -c 'exe \"normal! zt\"' -c %d %s"%( - row, - row + offset[1], - reader.get_current_filename() - ), - shell = True + "vim +%d -c 'exe \"normal! zt\"' -c %d %s" + % (row, row + offset[1], reader.get_current_filename()), + shell=True, ) -def take_screenshot(reader,opts): - out_file = os.path.join( - opts.screenshots, - "slide%03d.png"%(reader.page + 1,) - ) +def take_screenshot(reader, opts): + out_file = os.path.join(opts.screenshots, "slide%03d.png" % (reader.page + 1,)) if not os.path.exists(opts.screenshots): os.mkdir(opts.screenshots) subprocess.call( - "sleep 0.5; import -window %s '%s'"%( - opts.xwinid, - out_file - ), - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - shell = True + "sleep 0.5; import -window %s '%s'" % (opts.xwinid, out_file), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True, ) @@ -879,29 +985,26 @@ def main(): opts = setup_options() if opts.screenshots: print("Click the terminal window to read window ID") - xwininfo = subprocess.check_output( - "xwininfo", - shell = True - ) + xwininfo = subprocess.check_output("xwininfo", shell=True) if type(xwininfo) == bytes: - xwininfo = xwininfo.decode('utf-8') + xwininfo = xwininfo.decode("utf-8") xwininfo = re.search(r"Window id: (0x[0-9]+)", xwininfo) if xwininfo: opts.xwinid = xwininfo.group(1) else: print("Cannot parse window ID") sys.exit(1) - browser( - opts, - opts.files - ) + browser(opts, opts.files) print("\n\n") if opts.screenshots: - print("Crop the images for terminal tabs, and PDFize e.g.:\n- mogrify -chop 0x50 %s/*png\n- convert %s/*png %s.pdf"%( + print( + "Crop the images for terminal tabs, and PDFize e.g.:\n- mogrify -chop 0x50 %s/*png\n- convert %s/*png %s.pdf" + % ( opts.screenshots, opts.screenshots, os.path.basename(opts.screenshots), - )) + ) + ) if __name__ == "__main__": diff --git a/reporting/markslider.tar.gz b/reporting/markslider.tar.gz index 11ce773d8e7434c7203d258fce9198c996d64961..b3ccc182ee3d05e49794ced00001f64d0218b9dd 100644 GIT binary patch literal 12970 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 literal 12906 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 Uc{b1H`R70X2b+w7jsWlg0A}?T761SM diff --git a/reporting/md_color.py b/reporting/md_color.py index 975fb36..9b82aeb 100755 --- a/reporting/md_color.py +++ b/reporting/md_color.py @@ -1,108 +1,149 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -import sys,os,re +import sys, os, re from argparse import ArgumentParser, RawDescriptionHelpFormatter + sys.path.append(os.path.dirname(os.path.realpath(__file__))) import ansicodes as ansi __author__ = "Ville Rantanen " __version__ = "0.3" -''' Rules modified from mistune project ''' +""" Rules modified from mistune project """ + def setup_options(): bc = ansi.code() - ''' Create command line options ''' - usage = ''' + """ Create command line options """ + usage = ( + """ Markdown syntax color in ansi codes. Special syntaxes: - Colors: insert string e.g. ${C}. - Any ANSI control code: ${3A}, ${1;34;42m}, see the table.. -''' + ansi.demo() - - parser = ArgumentParser( - formatter_class = RawDescriptionHelpFormatter, - description = usage, - epilog = __author__ +""" + + ansi.demo() ) - parser.add_argument("-v","--version",action="version",version=__version__) - parser.add_argument("-D",action="store_true",dest="debug",default=False, - help="Debug mode") - parser.add_argument("--dc",action="store_true",dest="dark_colors",default=False, - help="Use dark colorscheme, better for white background terminals.") - parser.add_argument("--no-color","-n",action="store_false",dest="color",default=True, - help="Disable color.") - parser.add_argument("--print-template",action="store_true",dest="print_template",default=False, - help="Print customizable color template.") - parser.add_argument("--template",action="store",type=str,dest="template",default=None, - help="Use a custom template file for colorization.") - parser.add_argument("-z",action="store_true",dest="zero",default=False, - help="Reset coloring at the end of each line.") - parser.add_argument("-Z",action="store_false",dest="zero_final",default=True, - help="Disable reset of colors at the end of file.") - parser.add_argument("filename",type=str, nargs='?', - help="File to show, - for stdin") - opts=parser.parse_args() + parser = ArgumentParser( + formatter_class=RawDescriptionHelpFormatter, + description=usage, + epilog=__author__, + ) + + parser.add_argument("-v", "--version", action="version", version=__version__) + parser.add_argument( + "-D", action="store_true", dest="debug", default=False, help="Debug mode" + ) + parser.add_argument( + "--dc", + action="store_true", + dest="dark_colors", + default=False, + help="Use dark colorscheme, better for white background terminals.", + ) + parser.add_argument( + "--no-color", + "-n", + action="store_false", + dest="color", + default=True, + help="Disable color.", + ) + parser.add_argument( + "--print-template", + action="store_true", + dest="print_template", + default=False, + help="Print customizable color template.", + ) + parser.add_argument( + "--template", + action="store", + type=str, + dest="template", + default=None, + help="Use a custom template file for colorization.", + ) + parser.add_argument( + "-z", + action="store_true", + dest="zero", + default=False, + help="Reset coloring at the end of each line.", + ) + parser.add_argument( + "-Z", + action="store_false", + dest="zero_final", + default=True, + help="Disable reset of colors at the end of file.", + ) + parser.add_argument( + "filename", type=str, nargs="?", help="File to show, - for stdin" + ) + opts = parser.parse_args() return opts + def parse(data): - data=[[None,row] for row in data] - block='text' - new_block='text' - multiline_block=False + data = [[None, row] for row in data] + block = "text" + new_block = "text" + multiline_block = False # Parse styles - for i,line in enumerate(data): - row=line[1] + for i, line in enumerate(data): + row = line[1] if line[0] is not None: # Previous lines have set the style already continue for match in blocks: - if block_match[match]['re'].match(row): - new_block=match - if match.startswith('multiline'): + if block_match[match]["re"].match(row): + new_block = match + if match.startswith("multiline"): if multiline_block: - multiline_block=False + multiline_block = False else: - multiline_block=match + multiline_block = match break if multiline_block: - new_block=multiline_block + new_block = multiline_block # Lists must end with empty line - if new_block not in ('empty','list_bullet') and block.startswith('list_'): - new_block='list_loose' + if new_block not in ("empty", "list_bullet") and block.startswith("list_"): + new_block = "list_loose" - if 'mod' in block_match[match]: + if "mod" in block_match[match]: # Style sets block in previous or next lines - data[i+block_match[match]['mod']['pos']][0]=block_match[match]['mod']['name'] - data[i][0]=new_block - if new_block!=block: - block=new_block + data[i + block_match[match]["mod"]["pos"]][0] = block_match[match]["mod"][ + "name" + ] + data[i][0] = new_block + if new_block != block: + 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' + 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') + multiline_block = block.startswith("multiline") if multiline_block: 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 + if inline_match[match]["re"].search(row): + row = inline_match[match]["re"].sub( + inline_match[match][cs] + block_match[block][csc], row ) if remove_colors: colored = bc.nocolor_string(row) @@ -114,31 +155,34 @@ def colorize(data, remove_colors = False, dark_colors = False, debug = False): colorized.append(colored) return colorized + def md_re_compile(d): - ''' Returns a re.compiled dict ''' - n={} + """ Returns a re.compiled dict """ + n = {} for t in d: - n[t]={} + n[t] = {} for i in d[t]: - n[t][i]=d[t][i] + n[t][i] = d[t][i] try: - if n[t]['re']: - n[t]['re']=re.compile(n[t]['re']) + if n[t]["re"]: + n[t]["re"] = re.compile(n[t]["re"]) except err: - print("Error compiling: %s"%(n[t]['re'])) + print("Error compiling: %s" % (n[t]["re"])) sys.exit(1) return n + def read_data2(fp): data = [] # Read data for row in f: if not row: continue - row = row.decode('utf-8').rstrip("\n\r ") + row = row.decode("utf-8").rstrip("\n\r ") data.append(row) return data + def read_data3(fp): data = [] # Read data @@ -149,14 +193,16 @@ def read_data3(fp): data.append(row) return data + def write_colored2(colored, opts): for c in colored: - sys.stdout.write(c.encode('utf-8')) + sys.stdout.write(c.encode("utf-8")) if opts.zero: sys.stdout.write(bc.Z) sys.stdout.write("\n") if opts.zero_final: - sys.stdout.write(bc.Z.encode('utf-8')) + sys.stdout.write(bc.Z.encode("utf-8")) + def write_colored3(colored, opts): for c in colored: @@ -168,112 +214,207 @@ def write_colored3(colored, opts): sys.stdout.write(bc.Z) - # re: regular expression, bc: bright colors, bcc: continue with this color after inline # dc: dark colors, dcc: continued color after inline -block_match_str={ - 'block_code': {'re':'^( {4}[^*])(.*)$', - 'bc' :'${Z}${c}\\1\\2', 'bcc':'${Z}${c}', - 'dc' :'${Z}${m}\\1\\2', 'dcc':'${Z}${m}'}, # code - 'multiline_code' : {'re':'^ *(`{3,}|~{3,}) *(\S*)', - 'bc' :'${Z}${c}\\1\\2', 'bcc':'${Z}${c}', - 'dc' :'${Z}${m}\\1\\2', 'dcc':'${Z}${m}'}, # ```lang - 'block_quote': {'re':'^(>[ >]* )', - 'bc':'${K}\\1${Z}','bcc':'${Z}', - 'dc':'${Y}\\1${Z}','dcc':'${Z}'}, # > > quote - 'hrule': {'re':'^ {0,3}[-*_]([-*_]){2,}$', - 'bc':'False','bcc':'${Z}', - 'dc':'False','dcc':'${Z}'}, # ---- - 'heading' : {'re':'^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)', - 'bc':'${W}\\1 ${U}\\2${Z}','bcc':'${W}${U}', - 'dc':'${B}\\1 ${U}\\2${Z}','dcc':'${B}${U}'}, # # heading - 'lheading' : {'re':'^(=+|-+)$', - 'bc':'${W}\\1', 'bcc':'${W}', - 'dc':'${B}\\1', 'dcc':'${B}', - 'mod':{'pos':-1,'name':'lheading.mod'}}, # ======= under header - 'lheading.mod' : {'re':'^([^\n]+)', - 'bc':'${W}\\1', 'bcc':'${W}', - 'dc':'${B}\\1', 'dcc':'${B}'}, # over the ======= under header - 'list_bullet': {'re':'^( *)([*+-]|[\d\.]+)( +)', - 'bc':'\\1${y}\\2${Z}\\3','bcc':'${Z}', - 'dc':'\\1${r}\\2${Z}\\3','dcc':'${Z}'}, # * or 1. - 'list_loose': {'re':'None', - 'bc':'False','bcc':'${Z}', - 'dc':'False','dcc':'${Z}'}, - 'text': {'re':'^([^\n]+)', - 'bc':'${Z}\\1','bcc':'${Z}', - 'dc':'${Z}\\1','dcc':'${Z}'}, - 'empty': {'re':'(^$)', - 'bc':'${Z}\\1','bcc':'${Z}', - 'dc':'${Z}\\1','dcc':'${Z}'}, - 'preformatted': {'re': 'a^', # Never matches anything - 'bc':'','bcc':'', - 'dc':'','dcc':''}, +block_match_str = { + "block_code": { + "re": "^( {4}[^*])(.*)$", + "bc": "${Z}${c}\\1\\2", + "bcc": "${Z}${c}", + "dc": "${Z}${m}\\1\\2", + "dcc": "${Z}${m}", + }, # code + "multiline_code": { + "re": "^ *(`{3,}|~{3,}) *(\S*)", + "bc": "${Z}${c}\\1\\2", + "bcc": "${Z}${c}", + "dc": "${Z}${m}\\1\\2", + "dcc": "${Z}${m}", + }, # ```lang + "block_quote": { + "re": "^(>[ >]* )", + "bc": "${K}\\1${Z}", + "bcc": "${Z}", + "dc": "${Y}\\1${Z}", + "dcc": "${Z}", + }, # > > quote + "hrule": { + "re": "^ {0,3}[-*_]([-*_]){2,}$", + "bc": "False", + "bcc": "${Z}", + "dc": "False", + "dcc": "${Z}", + }, # ---- + "heading": { + "re": "^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)", + "bc": "${W}\\1 ${U}\\2${Z}", + "bcc": "${W}${U}", + "dc": "${B}\\1 ${U}\\2${Z}", + "dcc": "${B}${U}", + }, # # heading + "lheading": { + "re": "^(=+|-+)$", + "bc": "${W}\\1", + "bcc": "${W}", + "dc": "${B}\\1", + "dcc": "${B}", + "mod": {"pos": -1, "name": "lheading.mod"}, + }, # ======= under header + "lheading.mod": { + "re": "^([^\n]+)", + "bc": "${W}\\1", + "bcc": "${W}", + "dc": "${B}\\1", + "dcc": "${B}", + }, # over the ======= under header + "list_bullet": { + "re": "^( *)([*+-]|[\d\.]+)( +)", + "bc": "\\1${y}\\2${Z}\\3", + "bcc": "${Z}", + "dc": "\\1${r}\\2${Z}\\3", + "dcc": "${Z}", + }, # * or 1. + "list_loose": { + "re": "None", + "bc": "False", + "bcc": "${Z}", + "dc": "False", + "dcc": "${Z}", + }, + "text": { + "re": "^([^\n]+)", + "bc": "${Z}\\1", + "bcc": "${Z}", + "dc": "${Z}\\1", + "dcc": "${Z}", + }, + "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'] +block_match = md_re_compile(block_match_str) +blocks = [ + "block_quote", + "multiline_code", + "hrule", + "heading", + "lheading", + "list_bullet", + "block_code", + "text", + "empty", +] -inline_match_str={ - 'bold1': {'re':r'(^| |})(_[^_]+_)', - 'bc':'\\1${W}\\2','dc':'\\1${W}\\2'}, # _bold_ - 'bold2': {'re':r'(^| |})(\*{1,2}[^\*]+\*{1,2})', - 'bc':'\\1${W}\\2','dc':'\\1${W}\\2'}, # **bold** - 'code': {'re':r'([`]+[^`]+[`]+)', - 'bc':'${c}\\1','dc':'${m}\\1'}, # `code` - 'code_special': {'re':r'([`]+[^`]+[`]+)([!>])', - 'bc':'${c}\\1${g}\\2','dc':'${m}\\1${r}\\2'}, # `code`! or `code`> for markslider - 'link': {'re':r'(\[)([^\]]+)(\])\(([^\)]+)\)', - 'bc':'${B}\\1${Z}\\2${B}\\3(${U}\\4${u})', - 'dc':'${b}\\1${Z}\\2${b}\\3(${U}\\4${u})'}, # [text](link) - 'image': {'re':r'(!\[[^\]]+\]\([^\)]+\))', - 'bc':'${r}\\1','dc':'${g}\\1'}, # ![text](image) - 'underline': {'re':r'(^|\W)(__)([^_]+)(__)', - 'bc':'\\1\\2${U}\\3${Z}\\4','dc':'\\1\\2${U}\\3${Z}\\4'}, # __underline__ - 'strikethrough': {'re':r'(~~)([^~]+)(~~)', - 'bc':'\\1${st}\\2${so}\\3','dc':'\\1${st}\\2${so}\\3'}, # ~~strike~~ - 'checkbox': {'re':r'(\[[x ]\])', - 'bc':'${y}\\1','dc':'${r}\\1'}, # [x] [ ] +inline_match_str = { + "bold1": { + "re": r"(^| |})(_[^_]+_)", + "bc": "\\1${W}\\2", + "dc": "\\1${W}\\2", + }, # _bold_ + "bold2": { + "re": r"(^| |})(\*{1,2}[^\*]+\*{1,2})", + "bc": "\\1${W}\\2", + "dc": "\\1${W}\\2", + }, # **bold** + "code": {"re": r"([`]+[^`]+[`]+)", "bc": "${c}\\1", "dc": "${m}\\1"}, # `code` + "code_special": { + "re": r"([`]+[^`]+[`]+)([!>])", + "bc": "${c}\\1${g}\\2", + "dc": "${m}\\1${r}\\2", + }, # `code`! or `code`> for markslider + "link": { + "re": r"(\[)([^\]]+)(\])\(([^\)]+)\)", + "bc": "${B}\\1${Z}\\2${B}\\3(${U}\\4${u})", + "dc": "${b}\\1${Z}\\2${b}\\3(${U}\\4${u})", + }, # [text](link) + "image": { + "re": r"(!\[[^\]]+\]\([^\)]+\))", + "bc": "${r}\\1", + "dc": "${g}\\1", + }, # ![text](image) + "underline": { + "re": r"(^|\W)(__)([^_]+)(__)", + "bc": "\\1\\2${U}\\3${Z}\\4", + "dc": "\\1\\2${U}\\3${Z}\\4", + }, # __underline__ + "strikethrough": { + "re": r"(~~)([^~]+)(~~)", + "bc": "\\1${st}\\2${so}\\3", + "dc": "\\1${st}\\2${so}\\3", + }, # ~~strike~~ + "checkbox": {"re": r"(\[[x ]\])", "bc": "${y}\\1", "dc": "${r}\\1"}, # [x] [ ] } inline_match = md_re_compile(inline_match_str) -inlines = ['bold1','bold2','code_special','code','image','link','underline','strikethrough', 'checkbox'] +inlines = [ + "bold1", + "bold2", + "code_special", + "code", + "image", + "link", + "underline", + "strikethrough", + "checkbox", +] if __name__ == "__main__": - opts=setup_options() + opts = setup_options() if opts.print_template: import json - print(json.dumps({'about':'re: regular expression, bc: bright colors, bcc: continue with this color after inline, dc: dark colors, dcc: continued color after inline. "blocks" and "inlines" list keys of matchers. ', - 'blocks':blocks, - 'block_match':block_match_str, - 'inline_match':inline_match_str, - 'inlines':inlines}, - indent=2,sort_keys=True)) + + print( + json.dumps( + { + "about": 're: regular expression, bc: bright colors, bcc: continue with this color after inline, dc: dark colors, dcc: continued color after inline. "blocks" and "inlines" list keys of matchers. ', + "blocks": blocks, + "block_match": block_match_str, + "inline_match": inline_match_str, + "inlines": inlines, + }, + indent=2, + sort_keys=True, + ) + ) sys.exit(0) if opts.template: import json - template=json.load(open(opts.template,'r')) - if 'inlines' in template: inlines=template['inlines'] - if 'blocks' in template: blocks=template['blocks'] - if 'block_match' in template: - block_match_str=template['block_match'] - block_match=md_re_compile(block_match_str) - if 'inline_match' in template: - inline_match_str=template['inline_match'] - inline_match=md_re_compile(inline_match_str) + + template = json.load(open(opts.template, "r")) + if "inlines" in template: + inlines = template["inlines"] + if "blocks" in template: + blocks = template["blocks"] + if "block_match" in template: + block_match_str = template["block_match"] + block_match = md_re_compile(block_match_str) + if "inline_match" in template: + inline_match_str = template["inline_match"] + inline_match = md_re_compile(inline_match_str) bc = ansi.code() if opts.filename == "-" or opts.filename == None: f = sys.stdin else: - f = open(opts.filename, 'r') - if (sys.version_info > (3, 0)): + f = open(opts.filename, "r") + if sys.version_info > (3, 0): data = read_data3(f) else: data = read_data2(f) data = parse(data) colored = colorize(data, not opts.color, opts.dark_colors, opts.debug) - if (sys.version_info > (3, 0)): + if sys.version_info > (3, 0): write_colored3(colored, opts) else: write_colored2(colored, opts) - diff --git a/reporting/src2ans b/reporting/src2ans index a262204..c8ead41 100755 --- a/reporting/src2ans +++ b/reporting/src2ans @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # coding=utf-8 # # Copyright 2016 Ville Rantanen @@ -17,61 +17,83 @@ # along with this program. If not, see . # -'''Color by ${X} notation.''' +"""Color by ${X} notation.""" __author__ = "Ville Rantanen " __version__ = "0.1" -import sys,os,argparse,re +import sys, os, argparse, re from argparse import ArgumentParser + sys.path.append(os.path.dirname(os.path.realpath(__file__))) import ansicodes as ansi + def setup_options(): - ''' Create command line options ''' - usage=''' + """ Create command line options """ + usage = ( + """ Color notation renderer in ANSI codes Special syntaxes: * Colors: insert string ${X}, where X values in the table below. -''' + ansi.demo() +""" + + ansi.demo() + ) - parser=ArgumentParser(description=usage, - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=__author__) + parser = ArgumentParser( + description=usage, + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__author__, + ) - parser.add_argument("-v","--version",action="version",version=__version__) - parser.add_argument("--no-color","-n",action="store_false",dest="color",default=True, - help="Disable color.") - parser.add_argument("-z",action="store_true",dest="zero",default=False, - help="Reset coloring at the end of each line.") - parser.add_argument("-Z",action="store_false",dest="zero_final",default=True, - help="Disable reset of colors at the end of file.") - parser.add_argument("filename",type=str, - help="File to show, - for stdin") - opts=parser.parse_args() + parser.add_argument("-v", "--version", action="version", version=__version__) + parser.add_argument( + "--no-color", + "-n", + action="store_false", + dest="color", + default=True, + help="Disable color.", + ) + parser.add_argument( + "-z", + action="store_true", + dest="zero", + default=False, + help="Reset coloring at the end of each line.", + ) + parser.add_argument( + "-Z", + action="store_false", + dest="zero_final", + default=True, + help="Disable reset of colors at the end of file.", + ) + parser.add_argument("filename", type=str, help="File to show, - for stdin") + opts = parser.parse_args() return opts -bc=ansi.code() -opts=setup_options() -if opts.filename=="-": - f=sys.stdin + +bc = ansi.code() +opts = setup_options() +if opts.filename == "-": + f = sys.stdin else: - f=open(opts.filename,'r') + f = open(opts.filename, "r") for row in f: if not row: continue if type(row) == bytes: - row = row.decode('utf-8') - row=row.rstrip("\n\r ") + row = row.decode("utf-8") + row = row.rstrip("\n\r ") if opts.color: - colored=bc.color_string(row) + colored = bc.color_string(row) else: - colored=bc.nocolor_string(row) + colored = bc.nocolor_string(row) sys.stdout.write(colored) if opts.zero and opts.color: sys.stdout.write(bc.Z) sys.stdout.write("\n") if opts.zero_final and opts.color: sys.stdout.write(bc.Z) - diff --git a/reporting/srcframes2ans b/reporting/srcframes2ans index b4521ba..023a956 100755 --- a/reporting/srcframes2ans +++ b/reporting/srcframes2ans @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # coding=utf-8 # # Copyright 2016 Ville Rantanen @@ -17,115 +17,163 @@ # along with this program. If not, see . # -'''Color by notation.''' +"""Color by notation.""" __author__ = "Ville Rantanen " __version__ = "0.2" -NEWCHAR="====CHAR" -NEWCOLOR="====COLOR" -NEWIMAGE="====EOI" -ENDOFFILE="====EOF" -PAUSE="====PAUSE" +NEWCHAR = "====CHAR" +NEWCOLOR = "====COLOR" +NEWIMAGE = "====EOI" +ENDOFFILE = "====EOF" +PAUSE = "====PAUSE" + +import sys, os, argparse, re, time +from argparse import ArgumentParser + -import sys,os,argparse,re,time -from argparse import ArgumentParser class bc: - K="\033[1;30m" - R="\033[1;31m" - G="\033[1;32m" - Y="\033[1;33m" - B="\033[1;34m" - M="\033[1;35m" - C="\033[1;36m" - W="\033[1;37m" - - k="\033[0;30m" - r="\033[0;31m" - g="\033[0;32m" - y="\033[0;33m" - b="\033[0;34m" - m="\033[0;35m" - c="\033[0;36m" - w="\033[0;37m" - - nk="\033[30m" - nr="\033[31m" - ng="\033[32m" - ny="\033[33m" - nb="\033[34m" - nm="\033[35m" - nc="\033[36m" - nw="\033[37m" - - bk="\033[40m" - br="\033[41m" - bg="\033[42m" - by="\033[43m" - bb="\033[44m" - bm="\033[45m" - bc="\033[46m" - bw="\033[47m" - - S = '\033[1m' - s = '\033[2m'#strong off - U = '\033[4m'#underline - u = '\033[24m'#underline off - ic = '\033[7m'#inverse colors - io = '\033[27m'#inverse off - st = '\033[9m'#strike on - so = '\033[29m'#strike off - - Z = '\033[0m' - CLR = '\033[2J' - CLREND = '\033[K' - CLRSCR = CLR+"\033[0;0H" - - color_keys="pqwertyu01234567;asdfghjPQWERTYUxXczvVbBnN" - color_list=[K, R, G, Y, B, M, C, W, k, r, g, y, b, m, c, w, - bk,br,bg,by,bb,bm,bc,bw,nk,nr,ng,ny,nb,nm,nc,nw, - S,s,CLRSCR,Z,U,u,st,so,ic,io] + K = "\033[1;30m" + R = "\033[1;31m" + G = "\033[1;32m" + Y = "\033[1;33m" + B = "\033[1;34m" + M = "\033[1;35m" + C = "\033[1;36m" + W = "\033[1;37m" - def pos(self,y,x): - return "\033["+str(y)+";"+str(x)+"H" + k = "\033[0;30m" + r = "\033[0;31m" + g = "\033[0;32m" + y = "\033[0;33m" + b = "\033[0;34m" + m = "\033[0;35m" + c = "\033[0;36m" + w = "\033[0;37m" - def posprint(self, y,x,s): - sys.stdout.write( (self.pos(y,x) + str(s)).encode('utf-8') ) + nk = "\033[30m" + nr = "\033[31m" + ng = "\033[32m" + ny = "\033[33m" + nb = "\033[34m" + nm = "\033[35m" + nc = "\033[36m" + nw = "\033[37m" + + bk = "\033[40m" + br = "\033[41m" + bg = "\033[42m" + by = "\033[43m" + bb = "\033[44m" + bm = "\033[45m" + bc = "\033[46m" + bw = "\033[47m" + + S = "\033[1m" + s = "\033[2m" # strong off + U = "\033[4m" # underline + u = "\033[24m" # underline off + ic = "\033[7m" # inverse colors + io = "\033[27m" # inverse off + st = "\033[9m" # strike on + so = "\033[29m" # strike off + + Z = "\033[0m" + CLR = "\033[2J" + CLREND = "\033[K" + CLRSCR = CLR + "\033[0;0H" + + color_keys = "pqwertyu01234567;asdfghjPQWERTYUxXczvVbBnN" + color_list = [ + K, + R, + G, + Y, + B, + M, + C, + W, + k, + r, + g, + y, + b, + m, + c, + w, + bk, + br, + bg, + by, + bb, + bm, + bc, + bw, + nk, + nr, + ng, + ny, + nb, + nm, + nc, + nw, + S, + s, + CLRSCR, + Z, + U, + u, + st, + so, + ic, + io, + ] + + def pos(self, y, x): + return "\033[" + str(y) + ";" + str(x) + "H" + + def posprint(self, y, x, s): + sys.stdout.write((self.pos(y, x) + str(s)).encode("utf-8")) def clear(self): - sys.stdout.write( (self.CLR+self.pos(0,0)).encode('utf-8') ) - def clear_to_end(self): - sys.stdout.write( self.CLREND.encode('utf-8') ) + sys.stdout.write((self.CLR + self.pos(0, 0)).encode("utf-8")) - def color_char(self,s): - for i,c in enumerate(self.color_keys): + def clear_to_end(self): + sys.stdout.write(self.CLREND.encode("utf-8")) + + def color_char(self, s): + for i, c in enumerate(self.color_keys): if c in s: - s=s.replace(c,self.color_list[i]) + s = s.replace(c, self.color_list[i]) return s return "" - def nocolor_string(self,s): + + def nocolor_string(self, s): return "" + def end_drawing(opts): if opts.zero_final: - sys.stdout.write(bc.Z.encode('utf-8')) + sys.stdout.write(bc.Z.encode("utf-8")) sys.exit(0) + def open_data_file(opts): if opts.test_image: return test_data() - if opts.filename=='-': + if opts.filename == "-": return sys.stdin - return open(opts.filename,'r') + return open(opts.filename, "r") + def setup_options(): - ''' Create command line options ''' - usage=''' + """ Create command line options """ + usage = """ Color image renderer in ANSI codes. Example file: first layer of characters ====CHAR - more characters + more characters go here ====COLOR color codes go here (letters 1-7,0 and keys below like a piano) @@ -145,45 +193,75 @@ Color image renderer in ANSI codes. Example file: ====EOF end of file -''' - - parser=ArgumentParser(description=usage, - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=__author__) +""" - parser.add_argument("-v","--version",action="version",version=__version__) - parser.add_argument("--no-color","-n",action="store_false",dest="color",default=True, - help="Disable color.") - parser.add_argument("--test",action="store_true",dest="test_image",default=False, - help="Show test image showing features.") - parser.add_argument("-z",action="store_true",dest="zero",default=False, - help="Reset color codes at the end of each line.") - parser.add_argument("-Z",action="store_false",dest="zero_final",default=True, - help="Disable reset of colors at the end of file.") - parser.add_argument("-c",action="store_true",dest="clear",default=False, - help="Clear screen first") - parser.add_argument("filename",type=str, nargs='?', default=None, - help="File to show, - for stdin") - opts=parser.parse_args() - if not opts.test_image and opts.filename==None: + parser = ArgumentParser( + description=usage, + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__author__, + ) + + parser.add_argument("-v", "--version", action="version", version=__version__) + parser.add_argument( + "--no-color", + "-n", + action="store_false", + dest="color", + default=True, + help="Disable color.", + ) + parser.add_argument( + "--test", + action="store_true", + dest="test_image", + default=False, + help="Show test image showing features.", + ) + parser.add_argument( + "-z", + action="store_true", + dest="zero", + default=False, + help="Reset color codes at the end of each line.", + ) + parser.add_argument( + "-Z", + action="store_false", + dest="zero_final", + default=True, + help="Disable reset of colors at the end of file.", + ) + parser.add_argument( + "-c", + action="store_true", + dest="clear", + default=False, + help="Clear screen first", + ) + parser.add_argument( + "filename", type=str, nargs="?", default=None, help="File to show, - for stdin" + ) + opts = parser.parse_args() + if not opts.test_image and opts.filename == None: parser.error("Need a file or - as argument") - + return opts + def test_data(): - testimage=''' NOT VISIBLE - _ _ _ ____ ___ + testimage = ''' NOT VISIBLE + _ _ _ ____ ___ 1 red 5 magenta / \ | \ | / ___|_ _| - 2 green 6 cyan / _ \ | \| \___ \| | - 3 yellow 7 white / ___ \| |\ |___) | | + 2 green 6 cyan / _ \ | \| \___ \| | + 3 yellow 7 white / ___ \| |\ |___) | | 4 blue 0 black /_/ \_\_| \_|____/___| - - ,ad8888ba, 88 88888888ba - d8"' `"8b 88 88 "8b -d8' 88 88 ,8P + + ,ad8888ba, 88 88888888ba + d8"' `"8b 88 88 "8b +d8' 88 88 ,8P 88 ,adPPYba, 88 ,adPPYba, 88aaaaaa8P' ,adPPYba, 88 a8" "8a 88 a8" "8a 88""""88' I8[ "" -Y8, 8b d8 88 8b d8 88 `8b `"Y8ba, +Y8, 8b d8 88 8b d8 88 `8b `"Y8ba, Y8a. .a8P "8a, ,a8" 88 "8a, ,a8" 88 `8b aa ]8I `"Y8888Y"' `"YbbdP"' 8888888888 `"YbbdP"' 88 `8b `"YbbdP"' ====CHAR @@ -191,7 +269,7 @@ Y8, 8b d8 88 8b d8 88 `8b `"Y8ba, ====COLOR 1 2 3 4 5 6 7 0 q w e r t y u p Q W EXR TxY U P 1 - 1 5 1 + 1 5 1 2 6 1 3 7 1 R P Q @@ -221,98 +299,100 @@ u f u ; f U ; f U ====EOI -Outputs:'''.encode('utf-8') - return testimage.split('\n') +Outputs:'''.encode( + "utf-8" + ) + return testimage.split("\n") -bc=bc() -opts=setup_options() -f=open_data_file(opts) -endoffile=False + +bc = bc() +opts = setup_options() +f = open_data_file(opts) +endoffile = False if opts.clear: bc.clear() if opts.test_image: for row in f: print(row) while True: - pause=0 - gray=[] + pause = 0 + gray = [] gray.append([]) - colors=[] - colorFrame=0 - grayFrame=0 - maxRow=0 + colors = [] + colorFrame = 0 + grayFrame = 0 + maxRow = 0 for row in f: if not row: end_drawing(opts) - row=row.decode('utf-8').rstrip("\n\r") + row = row.decode("utf-8").rstrip("\n\r") if row.startswith(NEWIMAGE): break if row.startswith(ENDOFFILE): - endoffile=True + endoffile = True if endoffile: break if row.startswith(PAUSE): try: - new_value=row[len(PAUSE):].strip() - pause=float(new_value) + new_value = row[len(PAUSE) :].strip() + pause = float(new_value) except: - pause=0 + pause = 0 continue if row.startswith(NEWCOLOR): - colorFrame+=1 + colorFrame += 1 colors.append([]) continue - - if colorFrame==0: + + if colorFrame == 0: if row.startswith(NEWCHAR): - grayFrame+=1 + grayFrame += 1 gray.append([]) continue gray[grayFrame].append(row) - - maxRow=max(maxRow, len(row)) + + maxRow = max(maxRow, len(row)) else: - colors[colorFrame-1].append(row) - if len(gray[0])==0: + colors[colorFrame - 1].append(row) + if len(gray[0]) == 0: end_drawing(opts) for i in range(len(gray[0])): - while maxRow>len(gray[0][i]): - gray[0][i]=gray[0][i]+" " + while maxRow > len(gray[0][i]): + gray[0][i] = gray[0][i] + " " for i in range(len(gray[0])): for frame in colors: - if len(frame)len(frame[i]): - frame[i]=frame[i]+" " - if len(gray)>1: + if len(frame) < i + 1: + frame.append("") + while len(gray[0][i]) > len(frame[i]): + frame[i] = frame[i] + " " + if len(gray) > 1: for layer in gray[1:]: - if len(layer)len(layer[i]): - layer[i]=layer[i]+" " - + if len(layer) < i + 1: + layer.append("") + while len(gray[0][i]) > len(layer[i]): + layer[i] = layer[i] + " " for i in range(len(gray[0])): for c in range(len(gray[0][i])): if opts.color: - for frame in colors: + for frame in colors: try: - if frame[i][c]!=" ": - sys.stdout.write(bc.color_char(frame[i][c]).encode('utf-8')) + if frame[i][c] != " ": + sys.stdout.write(bc.color_char(frame[i][c]).encode("utf-8")) except IndexError: pass - char=" " + char = " " for layer in gray: - if layer[i][c]==" ": continue - char=layer[i][c] - - sys.stdout.write(char.encode('utf-8')) + if layer[i][c] == " ": + continue + char = layer[i][c] + + sys.stdout.write(char.encode("utf-8")) if opts.color and opts.zero: - sys.stdout.write(bc.Z.encode('utf-8')) + sys.stdout.write(bc.Z.encode("utf-8")) sys.stdout.write("\n") - if pause>0: + if pause > 0: time.sleep(pause) if opts.test_image: end_drawing(opts) -