import sublime import sublime_plugin from os import path import tempfile import sys import time import webbrowser import re from HtmlAnnotations import get_annotations import ExportHtmlLib.desktop as desktop import json PACKAGE_SETTINGS = "ExportHtml.sublime-settings" JS_DIR = path.join(sublime.packages_path(), 'ExportHtml', "js") CSS_DIR = path.join(sublime.packages_path(), 'ExportHtml', "css") if sublime.platform() == "linux": # Try and load Linux Python2.6 lib. Default path is for Ubuntu. linux_lib = sublime.load_settings(PACKAGE_SETTINGS).get("linux_python2.6_lib", "/usr/lib/python2.6/lib-dynload") if not linux_lib in sys.path and path.exists(linux_lib): sys.path.append(linux_lib) from plistlib import readPlist from ExportHtmlLib.rgba.rgba import RGBA FILTER_MATCH = re.compile(r'^(?:(brightness|saturation|hue|colorize)\((-?[\d]+|[\d]*\.[\d]+)\)|(sepia|grayscale|invert))$') # HTML Code HTML_HEADER = \ ''' %(title)s %(js)s ''' TOOL_GUTTER = '''''' TOOL_PLAIN_TEXT = '''''' TOOL_PRINT = '''''' TOOL_ANNOTATION = '''''' TOOL_DUMP_THEME = '''''' TOOL_WRAPPING = '''''' TOOLBAR = '''
%(options)s
''' ANNOTATE_OPEN = '''%(code)s''' ANNOTATE_CLOSE = '''''' BODY_START = '''
'''

FILE_INFO = '''
%(date_time)s %(file)s\n\n
''' TABLE_START = '''''' LINE = ( '' + '' + '' + '' ) CODE = '''%(content)s''' ANNOTATION_CODE = '''%(content)s''' TABLE_END = '''
' + '%(line)s ' + '' + '
%(code)s\n
' + '
''' ROW_START = '''''' ROW_END = '''''' DIVIDER = '''\n...\n\n''' ANNOTATION_TBL_START = ( '''' ANNOTATION_ROW = ( '' + '' + '%(link)s' + '' + '
%(comment)s
' + '' ) ANNOTATION_FOOTER = ( '' + '' + '' ) BODY_END = '''
%(toolbar)s\n%(js)s\n\n\n''' INCLUDE_THEME = \ ''' ''' TOGGLE_LINE_OPTIONS = \ ''' ''' AUTO_PRINT = \ ''' ''' WRAP = \ ''' ''' HTML_JS_WRAP = \ ''' ''' def getjs(file_name): code = "" try: with open(path.join(JS_DIR, file_name), "r") as f: code = f.read() except: pass return code def getcss(file_name, options): code = "" final_code = "" last_pt = 0 keys = '|'.join(options.keys()) replace = re.compile("/\\* *%(" + keys + ")% * \\*/") try: with open(path.join(CSS_DIR, file_name), "r") as f: code = f.read() for m in replace.finditer(code): final_code += code[last_pt:m.start()] + options[m.group(1)] last_pt = m.end() final_code += code[last_pt:] except: pass return final_code class ExportHtmlPanelCommand(sublime_plugin.WindowCommand): def execute(self, value): if value >= 0: view = self.window.active_view() if view != None: ExportHtml(view).run(**self.args[value]) def run(self): options = sublime.load_settings(PACKAGE_SETTINGS).get("html_panel", {}) menu = [] self.args = [] for opt in options: k, v = opt.items()[0] menu.append(k) self.args.append(v) if len(menu): self.window.show_quick_panel( menu, self.execute ) class ExportHtmlCommand(sublime_plugin.WindowCommand): def run(self, **kwargs): view = self.window.active_view() if view != None: ExportHtml(view).run(**kwargs) class ExportHtml(object): def __init__(self, view): self.view = view def process_inputs(self, **kwargs): return { "numbers": bool(kwargs.get("numbers", False)), "highlight_selections": bool(kwargs.get("highlight_selections", False)), "browser_print": bool(kwargs.get("browser_print", False)), "color_scheme": kwargs.get("color_scheme", None), "wrap": kwargs.get("wrap", None), "multi_select": bool(kwargs.get("multi_select", False)), "style_gutter": bool(kwargs.get("style_gutter", True)), "no_header": bool(kwargs.get("no_header", False)), "date_time_format": kwargs.get("date_time_format", "%m/%d/%y %I:%M:%S"), "show_full_path": bool(kwargs.get("show_full_path", True)), "toolbar": kwargs.get("toolbar", ["plain_text", "gutter", "wrapping", "print", "annotation", "theme"]), "save_location": kwargs.get("save_location", None), "time_stamp": kwargs.get("time_stamp", "_%m%d%y%H%M%S"), "clipboard_copy": bool(kwargs.get("clipboard_copy", False)), "view_open": bool(kwargs.get("view_open", False)), "shift_brightness": bool(kwargs.get("shift_brightness", False)), "filter": kwargs.get("filter", "") } def setup(self, **kwargs): path_packages = sublime.packages_path() # Get get general document preferences from sublime preferences eh_settings = sublime.load_settings(PACKAGE_SETTINGS) settings = sublime.load_settings('Preferences.sublime-settings') alternate_font_size = eh_settings.get("alternate_font_size", False) alternate_font_face = eh_settings.get("alternate_font_face", False) self.font_size = settings.get('font_size', 10) if alternate_font_size == False else alternate_font_size self.font_face = settings.get('font_face', 'Consolas') if alternate_font_face == False else alternate_font_face self.tab_size = settings.get('tab_size', 4) self.padd_top = settings.get('line_padding_top', 0) self.padd_bottom = settings.get('line_padding_bottom', 0) self.char_limit = int(eh_settings.get("valid_selection_size", 4)) self.bground = '' self.fground = '' self.gbground = '' self.gfground = '' self.sbground = '' self.sfground = '' self.numbers = kwargs["numbers"] self.date_time_format = kwargs["date_time_format"] self.time = time.localtime() self.show_full_path = kwargs["show_full_path"] self.highlight_selections = kwargs["highlight_selections"] self.browser_print = kwargs["browser_print"] self.auto_wrap = kwargs["wrap"] != None and int(kwargs["wrap"]) > 0 self.wrap = 900 if not self.auto_wrap else int(kwargs["wrap"]) self.hl_continue = None self.curr_hl = None self.sels = [] self.multi_select = self.check_sel() if kwargs["multi_select"] and not kwargs["highlight_selections"] else False self.size = self.view.size() self.pt = 0 self.end = 0 self.curr_row = 0 self.tables = 0 self.curr_annot = None self.curr_comment = None self.annotations = self.get_annotations() self.annot_num = -1 self.new_annot = False self.open_annot = False self.no_header = kwargs["no_header"] self.annot_tbl = [] self.toolbar = kwargs["toolbar"] self.toolbar_orientation = "block" if eh_settings.get("toolbar_orientation", "horizontal") == "vertical" else "inline-block" self.matched = {} self.ebground = self.bground self.dark_lumens = None self.lumens_limit = float(eh_settings.get("bg_min_lumen_threshold", 62)) self.filter = [] for f in kwargs["filter"].split(";"): m = FILTER_MATCH.match(f) if m: if m.group(1): self.filter.append((m.group(1), float(m.group(2)))) else: self.filter.append((m.group(3), 0.0)) fname = self.view.file_name() if fname == None or not path.exists(fname): fname = "Untitled" self.file_name = fname # Get color scheme if kwargs["color_scheme"] != None: alt_scheme = kwargs["color_scheme"] else: alt_scheme = eh_settings.get("alternate_scheme", False) scheme_file = settings.get('color_scheme') if alt_scheme == False else alt_scheme colour_scheme = path.normpath(scheme_file) self.scheme_file = path.basename(colour_scheme) self.plist_file = self.apply_filters(readPlist(path_packages + colour_scheme.replace('Packages', ''))) colour_settings = self.plist_file["settings"][0]["settings"] # Get general theme colors from color scheme file self.bground = self.strip_transparency(colour_settings.get("background", '#FFFFFF'), True, True) self.fground = self.strip_transparency(colour_settings.get("foreground", '#000000')) self.sbground = self.strip_transparency(colour_settings.get("selection", self.fground), True) self.sfground = self.strip_transparency(colour_settings.get("selectionForeground", None)) self.gbground = self.strip_transparency(colour_settings.get("gutter", self.bground)) if kwargs["style_gutter"] else self.bground self.gfground = self.strip_transparency(colour_settings.get("gutterForeground", self.fground), True) if kwargs["style_gutter"] else self.fground self.highlights = [] if self.highlight_selections: for sel in self.view.sel(): if not sel.empty(): self.highlights.append(sel) # Create scope colors mapping from color scheme file self.colours = {self.view.scope_name(self.end).split(' ')[0]: {"color": self.fground, "bgcolor": None, "style": None}} for item in self.plist_file["settings"]: scope = item.get('scope', None) colour = None style = [] if 'scope' in item: scope = item['scope'] if 'settings' in item: colour = item['settings'].get('foreground', None) bgcolour = item['settings'].get('background', None) if 'fontStyle' in item['settings']: for s in item['settings']['fontStyle'].split(' '): if s == "bold" or s == "italic": # or s == "underline": style.append(s) if scope != None and (colour != None or bgcolour != None): self.colours[scope] = { "color": self.strip_transparency(colour), "bgcolor": self.strip_transparency(bgcolour, True), "style": style } self.shift_brightness = kwargs["shift_brightness"] and self.dark_lumens is not None and self.dark_lumens < self.lumens_limit if self.shift_brightness: self.color_adjust() def apply_filters(self, tmtheme): def filter_color(color): rgba = RGBA(color) for f in self.filter: name = f[0] value = f[1] if name == "grayscale": rgba.grayscale() elif name == "sepia": rgba.sepia() elif name == "saturation": rgba.saturation(value) elif name == "invert": rgba.invert() elif name == "brightness": rgba.brightness(value) elif name == "hue": rgba.hue(value) elif name == "colorize": rgba.colorize(value) return rgba.get_rgba() if len(self.filter): general_settings_read = False for settings in tmtheme["settings"]: if not general_settings_read: for k, v in settings["settings"].items(): try: settings["settings"][k] = filter_color(v) except: pass general_settings_read = True continue try: settings["settings"]["foreground"] = filter_color(settings["settings"]["foreground"]) except: pass try: settings["settings"]["background"] = filter_color(settings["settings"]["background"]) except: pass return tmtheme def color_adjust(self): factor = 1 + ((self.lumens_limit - self.dark_lumens) / 255.0) if self.shift_brightness else None for k, v in self.colours.items(): fg, bg = v["color"], v["bgcolor"] if v["color"] is not None: self.colours[k]["color"] = self.apply_color_change(v["color"], factor) if v["bgcolor"] is not None: self.colours[k]["bgcolor"] = self.apply_color_change(v["bgcolor"], factor) self.bground = self.apply_color_change(self.bground, factor) self.fground = self.apply_color_change(self.fground, factor) self.sbground = self.apply_color_change(self.sbground, factor) if self.sfground is not None: self.sfground = self.apply_color_change(self.sfground, factor) self.gbground = self.apply_color_change(self.gbground, factor) self.gfground = self.apply_color_change(self.gfground, factor) def apply_color_change(self, color, shift_factor): rgba = RGBA(color) if shift_factor is not None: rgba.brightness(shift_factor) return rgba.get_rgb() def get_tools(self, tools, use_annotation, use_wrapping): toolbar_options = { "gutter": TOOL_GUTTER, "print": TOOL_PRINT, "plain_text": TOOL_PLAIN_TEXT, "annotation": TOOL_ANNOTATION if use_annotation else "", "theme": TOOL_DUMP_THEME, "wrapping": TOOL_WRAPPING if use_wrapping else "" } t_opt = "" toolbar_element = "" if len(tools): for t in tools: if t in toolbar_options: t_opt += toolbar_options[t] toolbar_element = TOOLBAR % {"options": t_opt} return toolbar_element def strip_transparency(self, color, track_darkness=False, simple_strip=False): if color is None: return color rgba = RGBA(color.replace(" ", "")) if not simple_strip: rgba.apply_alpha(self.bground if self.bground != "" else "#FFFFFF") if track_darkness: lumens = rgba.luminance() if self.dark_lumens is None or lumens < self.dark_lumens: self.dark_lumens = lumens return rgba.get_rgb() def setup_print_block(self, curr_sel, multi=False): # Determine start and end points and whether to parse whole file or selection if not multi and (curr_sel.empty() or self.highlight_selections or curr_sel.size() <= self.char_limit): self.size = self.view.size() self.pt = 0 self.end = 1 self.curr_row = 1 else: self.size = curr_sel.end() self.pt = curr_sel.begin() self.end = self.pt + 1 self.curr_row = self.view.rowcol(self.pt)[0] + 1 self.start_line = self.curr_row self.gutter_pad = len(str(self.view.rowcol(self.size)[0])) + 1 def check_sel(self): multi = False for sel in self.view.sel(): if not sel.empty() and sel.size() >= self.char_limit: multi = True self.sels.append(sel) return multi def print_line(self, line, num): html_line = LINE % { "line_id": num, "color": self.gfground, "bgcolor": self.gbground, "line": str(num).rjust(self.gutter_pad).replace(" ", ' '), "code_id": num, "code": line, "table": self.tables, "pad_color": self.ebground or self.bground } return html_line def guess_colour(self, pt, the_key): the_colour = self.fground the_bgcolour = None the_style = set([]) if the_key in self.matched: the_colour = self.matched[the_key]["color"] the_style = self.matched[the_key]["style"] the_bgcolour = self.matched[the_key]["bgcolor"] else: best_match_bg = 0 best_match_fg = 0 best_match_style = 0 for key in self.colours: match = self.view.score_selector(pt, key) if self.colours[key]["color"] is not None and match > best_match_fg: best_match_fg = match the_colour = self.colours[key]["color"] if self.colours[key]["style"] is not None and match > best_match_style: best_match_style = match for s in self.colours[key]["style"]: the_style.add(s) if self.colours[key]["bgcolor"] is not None and match > best_match_bg: best_match_bg = match the_bgcolour = self.colours[key]["bgcolor"] self.matched[the_key] = {"color": the_colour, "bgcolor": the_bgcolour, "style": the_style} if len(the_style) == 0: the_style = "normal" else: the_style = ' '.join(the_style) return the_colour, the_style, the_bgcolour def write_header(self, the_html): header = HTML_HEADER % { "title": path.basename(self.file_name), "css": getcss( 'export.css', { "font_size": str(self.font_size), "font_face": '"' + self.font_face + '"', "page_bg": self.bground, "gutter_bg": self.gbground, "body_fg": self.fground, "display_mode": 'table-cell' if self.numbers else 'none', "dot_color": self.fground, "toolbar_orientation": self.toolbar_orientation } ), "js": INCLUDE_THEME % { "jscode": getjs('plist.js'), "theme": json.dumps(self.plist_file, sort_keys=True, indent=4, separators=(',', ': ')).encode('raw_unicode_escape'), "name": self.scheme_file, } } the_html.write(header) def convert_view_to_html(self, the_html): for line in self.view.split_by_newlines(sublime.Region(self.pt, self.size)): self.size = line.end() empty = not bool(line.size()) line = self.convert_line_to_html(the_html, empty) the_html.write(self.print_line(line, self.curr_row)) self.curr_row += 1 def html_encode(self, text): # Format text to HTML encode_table = { '&': '&', '>': '>', '<': '<', '\t': ' ' * self.tab_size, '\n': '' } return re.sub( r'(?!\s($|\S))\s', ' ', ''.join( encode_table.get(c, c) for c in text ).encode('ascii', 'xmlcharrefreplace') ) def get_annotations(self): annotations = get_annotations(self.view) comments = [] for x in range(0, int(annotations["count"])): region = annotations["annotations"]["html_annotation_%d" % x]["region"] comments.append((region, annotations["annotations"]["html_annotation_%d" % x]["comment"])) comments.sort() return comments def annotate_text(self, line, the_colour, the_bgcolour, the_style, empty): pre_text = None annot_text = None post_text = None start = None # Pretext Check if self.pt >= self.curr_annot.begin(): # Region starts with an annotation start = self.pt else: # Region has text before annoation pre_text = self.html_encode(self.view.substr(sublime.Region(self.pt, self.curr_annot.begin()))) start = self.curr_annot.begin() if self.end == self.curr_annot.end(): # Region ends annotation annot_text = self.html_encode(self.view.substr(sublime.Region(start, self.end))) self.curr_annot = None elif self.end > self.curr_annot.end(): # Region has text following annotation annot_text = self.html_encode(self.view.substr(sublime.Region(start, self.curr_annot.end()))) post_text = self.html_encode(self.view.substr(sublime.Region(self.curr_annot.end(), self.end))) self.curr_annot = None else: # Region ends but annotation is not finished annot_text = self.html_encode(self.view.substr(sublime.Region(start, self.end))) self.curr_annot = sublime.Region(self.end, self.curr_annot.end()) # Print the separate parts pre text, annotation, post text if pre_text != None: self.format_text(line, pre_text, the_colour, the_bgcolour, the_style, empty) if annot_text != None: self.format_text(line, annot_text, the_colour, the_bgcolour, the_style, empty, annotate=True) if self.curr_annot == None: self.curr_comment = None if post_text != None: self.format_text(line, post_text, the_colour, the_bgcolour, the_style, empty) def add_annotation_table_entry(self): row, col = self.view.rowcol(self.annot_pt) self.annot_tbl.append( ( self.tables, self.curr_row, "Line %d Col %d" % (row + 1, col + 1), self.curr_comment.encode('ascii', 'xmlcharrefreplace') ) ) self.annot_pt = None def format_text(self, line, text, the_colour, the_bgcolour, the_style, empty, annotate=False): if empty: text = ' ' else: the_style += " real_text" if the_bgcolour is None: the_bgcolour = self.bground if annotate: code = ANNOTATION_CODE % {"highlight": the_bgcolour, "color": the_colour, "content": text, "class": the_style} else: code = CODE % {"highlight": the_bgcolour, "color": the_colour, "content": text, "class": the_style} if annotate: if self.curr_annot != None and not self.open_annot: # Open an annotation if self.annot_pt != None: self.add_annotation_table_entry() if self.new_annot: self.annot_num += 1 self.new_annot = False code = ANNOTATE_OPEN % {"code": code, "comment": str(self.annot_num)} self.open_annot = True elif self.curr_annot == None: if self.open_annot: # Close an annotation code += ANNOTATE_CLOSE self.open_annot = False else: # Do a complete annotation if self.annot_pt != None: self.add_annotation_table_entry() if self.new_annot: self.annot_num += 1 self.new_annot = False code = ( ANNOTATE_OPEN % {"code": code, "comment": str(self.annot_num)} + ANNOTATE_CLOSE ) line.append(code) def convert_line_to_html(self, the_html, empty): line = [] hl_done = False # Continue highlight form last line if self.hl_continue != None: self.curr_hl = self.hl_continue self.hl_continue = None while self.end <= self.size: # Get next highlight region if self.highlight_selections and self.curr_hl == None and len(self.highlights) > 0: self.curr_hl = self.highlights.pop(0) # See if we are starting a highlight region if self.curr_hl != None and self.pt == self.curr_hl.begin(): # Get text of like scope up to a highlight scope_name = self.view.scope_name(self.pt) while self.view.scope_name(self.end) == scope_name and self.end < self.size: # Kick out if we hit a highlight region if self.end == self.curr_hl.end(): break self.end += 1 if self.end < self.curr_hl.end(): if self.end >= self.size: self.hl_continue = sublime.Region(self.end, self.curr_hl.end()) else: self.curr_hl = sublime.Region(self.end, self.curr_hl.end()) else: hl_done = True if hl_done and empty: the_colour, the_style, the_bgcolour = self.guess_colour(self.pt, scope_name) elif self.sfground is None: the_colour, the_style, _ = self.guess_colour(self.pt, scope_name) the_bgcolour = self.sbground else: the_colour, the_style = self.sfground, "normal" the_bgcolour = self.sbground else: # Get text of like scope up to a highlight scope_name = self.view.scope_name(self.pt) while self.view.scope_name(self.end) == scope_name and self.end < self.size: # Kick out if we hit a highlight region if self.curr_hl != None and self.end == self.curr_hl.begin(): break self.end += 1 the_colour, the_style, the_bgcolour = self.guess_colour(self.pt, scope_name) # Get new annotation if (self.curr_annot == None or self.curr_annot.end() < self.pt) and len(self.annotations): self.curr_annot, self.curr_comment = self.annotations.pop(0) self.annot_pt = self.curr_annot[0] while self.pt > self.curr_annot[1]: if len(self.annotations): self.curr_annot, self.curr_comment = self.annotations.pop(0) self.annot_pt = self.curr_annot[0] else: self.curr_annot = None self.curr_comment = None break self.new_annot = True self.curr_annot = sublime.Region(self.curr_annot[0], self.curr_annot[1]) region = sublime.Region(self.pt, self.end) if self.curr_annot != None and region.intersects(self.curr_annot): # Apply annotation within the text and format the text self.annotate_text(line, the_colour, the_bgcolour, the_style, empty) else: # Normal text formatting tidied_text = self.html_encode(self.view.substr(region)) self.format_text(line, tidied_text, the_colour, the_bgcolour, the_style, empty) if hl_done: # Clear highlight flags and variables hl_done = False self.curr_hl = None # Continue walking through line self.pt = self.end self.end = self.pt + 1 # Close annotation if open at end of line if self.open_annot: line.append(ANNOTATE_CLOSE % {"comment": self.curr_comment}) self.open_annot = False # Get the color for the space at the end of a line if self.end < self.view.size(): end_key = self.view.scope_name(self.pt) _, _, self.ebground = self.guess_colour(self.pt, end_key) # Join line segments return ''.join(line) def write_body(self, the_html): processed_rows = "" the_html.write(BODY_START) the_html.write(TABLE_START) if not self.no_header: # Write file name date_time = time.strftime(self.date_time_format, self.time) the_html.write( FILE_INFO % { "bgcolor": self.bground, "color": self.fground, "date_time": date_time, "file": self.file_name if self.show_full_path else path.basename(self.file_name) } ) the_html.write(ROW_START) the_html.write(TABLE_START) # Convert view to HTML if self.multi_select: count = 0 total = len(self.sels) for sel in self.sels: self.setup_print_block(sel, multi=True) processed_rows += "[" + str(self.curr_row) + "," self.convert_view_to_html(the_html) count += 1 self.tables = count processed_rows += str(self.curr_row) + "]," if count < total: the_html.write(TABLE_END) the_html.write(ROW_END) the_html.write(ROW_START) the_html.write(DIVIDER % {"color": self.fground}) the_html.write(ROW_END) the_html.write(ROW_START) the_html.write(TABLE_START) else: self.setup_print_block(self.view.sel()[0]) processed_rows += "[" + str(self.curr_row) + "," self.convert_view_to_html(the_html) processed_rows += str(self.curr_row) + "]," self.tables += 1 the_html.write(TABLE_END) the_html.write(ROW_END) the_html.write(TABLE_END) js_options = [] if len(self.annot_tbl): self.add_comments_table(the_html) js_options.append(HTML_JS_WRAP % {"jscode": getjs('annotation.js')}) # Write javascript snippets js_options.append(HTML_JS_WRAP % {"jscode": getjs('print.js')}) js_options.append(HTML_JS_WRAP % {"jscode": getjs('plaintext.js')}) js_options.append(TOGGLE_LINE_OPTIONS % { "jscode": getjs('lines.js'), "wrap_size": self.wrap, "ranges": processed_rows.rstrip(','), "tables": self.tables, "header": ("false" if self.no_header else "true"), "gutter": ('true' if self.numbers else 'false') } ) if self.auto_wrap: js_options.append(WRAP) if self.browser_print: js_options.append(AUTO_PRINT) # Write empty line to allow copying of last line and line number without issue the_html.write(BODY_END % {"js": ''.join(js_options), "toolbar": self.get_tools(self.toolbar, len(self.annot_tbl), self.auto_wrap)}) def add_comments_table(self, the_html): the_html.write(ANNOTATION_TBL_START) the_html.write(''.join([ANNOTATION_ROW % {"table": t, "row": r, "link": l, "comment": c} for t, r, l, c in self.annot_tbl])) the_html.write(ANNOTATION_FOOTER) the_html.write(ANNOTATION_TBL_END) def run(self, **kwargs): inputs = self.process_inputs(**kwargs) self.setup(**inputs) save_location = inputs["save_location"] time_stamp = inputs["time_stamp"] if save_location is not None: fname = self.view.file_name() if ( ((fname == None or not path.exists(fname)) and save_location == ".") or not path.exists(save_location) or not path.isdir(save_location) ): html_file = ".html" save_location = None elif save_location == ".": html_file = "%s%s.html" % (fname, time.strftime(time_stamp, self.time)) elif fname is None or not path.exists(fname): html_file = path.join(save_location, "Untitled%s.html" % time.strftime(time_stamp, self.time)) else: html_file = path.join(save_location, "%s%s.html" % (path.basename(fname), time.strftime(time_stamp, self.time))) else: html_file = ".html" if save_location is not None: open_html = lambda x: open(x, "w") else: open_html = lambda x: tempfile.NamedTemporaryFile(delete=False, suffix=x) with open_html(html_file) as the_html: self.write_header(the_html) self.write_body(the_html) if inputs["clipboard_copy"]: the_html.seek(0) sublime.set_clipboard(the_html.read()) sublime.status_message("Export to HTML: copied to clipboard") if inputs["view_open"]: self.view.window().open_file(the_html.name) else: # Open in web browser; check return code, if failed try webbrowser status = desktop.open(the_html.name, status=True) if not status: webbrowser.open(the_html.name, new=2)