Files
ChocolateyPackages/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtml.py
Iristyle 5ed4214a22 feat(ST2.EditorPackages): bump up all packages
- Refresh PackageCache with latest versions of everything
2013-09-16 22:29:05 -04:00

915 lines
39 KiB
Python

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 = \
'''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>%(title)s</title>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<style type="text/css">
%(css)s
</style>
%(js)s
</head>
'''
TOOL_GUTTER = '''<img onclick="toggle_gutter();" alt="" title="Toggle Gutter" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AofFg8FBLseHgAAAAxpVFh0Q29tbWVudAAAAAAAvK6ymQAAAM5JREFUOMvdjzFqAlEQhuephZWNYEq9wJ4kt7C08AiyhWCXQI6xh5CtUqVJEbAU1kA6i92VzWPnmxR5gUUsfAQbf5himPm+YURumSzLetFQWZZj4Bn45DcHYFMUxfAqAbA1MwO+gHeA0L9cJfDeJ6q6yvO8LyKiqosgOKVp6qJf8t4nQfD9J42Kqi6D4DUabppmBhzNzNq2fYyC67p+AHbh+iYKrqpqAnwE+Ok/8Dr6b+AtwArsu6Wq8/P9wQXHTETEOdcTkWl3YGYjub/8ANrnvguZ++ozAAAAAElFTkSuQmCC" />'''
TOOL_PLAIN_TEXT = '''<img onclick="toggle_plain_text();" alt="" title="Toggle Plain" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AofFg8dF9eGSAAAAAxpVFh0Q29tbWVudAAAAAAAvK6ymQAAANRJREFUOMvdkTFOgkEQhResaeQScAsplIRGDgKttnsIOYIUm1hQa/HfQiMFcIBt9k/UTWa/sRkbspKfzviS7eZ7M/uec39WbdsOgU/gI6V0ebZBKeVeTaWUu7PgpmkugD3wZW8XQuh3NhCRuaoq8AisVVVF5LazAfBi0JWITMzsuROccx4b8O6cc977HrAFyDmPumxfHQf3EyjwcBKOMQ6ApL8ISDHGwanqljb4VLlsY5ctqrD99c3Cm1aamZn5q/e+V6vuxgb2tc5DCH3gYAuu3f/RNzmJ99G3cZ53AAAAAElFTkSuQmCC" />'''
TOOL_PRINT = '''<img onclick="page_print();" alt="" title="Print" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AofFhAl8o8wSAAAAAxpVFh0Q29tbWVudAAAAAAAvK6ymQAAAQZJREFUOMulkzFSgjEUhL8X/xJ/iBegkAtQ6A08gIUehFthYykOw9BzBmwcaxGoZW1eZjIx/sC4TTJv8ja7mxfDIcmAAWDUIeDLzJQXm2w/AFZOkB8yoAU+gHtJ7yVJUnAlaS1pJOm6WN8kjSUtJQ1dLQChIvMATIEXXw9e3wMT4NnV/rKQsAcegAvgG9g5wcwv7OU51QgugSegD2yBO+DWmyLw+leINQXyW5VeoQj4qIIcW+CxPFwjSLKtEnAZOkGSSYruL3QMUxq0AERJUZKl5pV7bjsmMR+qHfAJ3DReDB4cwKLiv0R0S5Yy6ANz37ecgSZ7nui1zYm9G0B2wi+k63fyX/wA0b9vjF8iB3oAAAAASUVORK5CYII=" />'''
TOOL_ANNOTATION = '''<img onclick="toggle_annotations();" alt="" title="Toggle Annotations" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AofFhAIt1BsPQAAAAxpVFh0Q29tbWVudAAAAAAAvK6ymQAAALVJREFUOMvNkkESgjAUQ/Or+8IN9A7ewHN7FRgvwIB7eW6+WGsRRjZm05n+Jn+aRNoIyy8Ak1QVZkjqzYyiQEKsJF0kxUxgkHSW1Eu6mdn9bStwABqgA0Y+MfqsBU7ALie3M8SS0NU5JqD2zWvIqUgD1MF9iCVDF8yPkixsjTF4PIOfa/Hi/GhiO5mYJHH0mL4ROzdvIu+TAoW8ddm30iJNjTQgevOqpMLPx8NilWe6X3z8n3gAfmBJ5rRJVyQAAAAASUVORK5CYII=" />'''
TOOL_DUMP_THEME = '''<img onclick="dump_theme();" alt="" title="Download" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AofFhAWTV9RXgAAAAxpVFh0Q29tbWVudAAAAAAAvK6ymQAAAJtJREFUOMvdk9ENwyAQQ5+rDBA6QZbI/gN0h3YE2gXi/lykhABN1b9aQkh3+CEwiEK2BYyAyhbwlORtceCoEbgBqahnYI65C1CYr43eThd+1B8Ahkp0qXZZa8/2LlIFIG2i676DmDMwS8pDcZzW7tt4DbwOr8/2ZPthe3FbS6yZ4thfQdrmE5DP5g7kvLkCucdomtWDRJzUvvGqN6JK1cOooSjlAAAAAElFTkSuQmCC" />'''
TOOL_WRAPPING = '''<img onclick="toggle_wrapping();" alt="" title="Toggle Wrapping" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AsBFiYl9jWoIQAAAAxpVFh0Q29tbWVudAAAAAAAvK6ymQAAAP1JREFUOMudk0FuwkAMRZ+jbCGZXADlKvS8HKG9Qa8Q1K5LSLpG+ixwUmdKQMLSSDOeb/v7e8bITJIBNWD5FTCYmaLT7gTWwDtQZQlG4A0YYiILwTvgwxNsgV+vOuEm3wDsga+ZjaQkqZN0kdT7vpXU+Grd1zumk5Rm6g44STr6PjmriEl+d3RsK8li9T/nimXFOkmp8P4mwcZc5YXit7vRjxVgBQ/MK1aPWBWu9Jw1A9c+mZ0nW7AFdLevwKCR9BPE/SdiaWaSNADntdb9jXz6eQt8L15lGFM+vsarRevjtMqg7pkXrHghZiFs+QS8xmwDHIC9PXsHK197/t5XQswlGeOCYgkAAAAASUVORK5CYII=" />'''
TOOLBAR = '''<div id="toolbarhide"><div id="toolbar">%(options)s</div></div>'''
ANNOTATE_OPEN = '''<span onclick="toggle_annotations();" class="tooltip_hotspot" onmouseover="tooltip.show(%(comment)s);" onmouseout="tooltip.hide();">%(code)s'''
ANNOTATE_CLOSE = '''</span>'''
BODY_START = '''<body class="code_page code_text"><pre class="code_page">'''
FILE_INFO = '''<tr><td colspan="2" style="background: %(bgcolor)s"><div id="file_info"><span style="color: %(color)s">%(date_time)s %(file)s\n\n</span></div></td></tr>'''
TABLE_START = '''<table cellspacing="0" cellpadding="0" class="code_page">'''
LINE = (
'<tr>' +
'<td valign="top" id="L_%(table)d_%(line_id)d" class="code_text code_gutter" style="background: %(bgcolor)s">' +
'<span style="color: %(color)s;">%(line)s&nbsp;</span>' +
'</td>' +
'<td valign="top" class="code_text code_line" style="background-color: %(pad_color)s;">' +
'<div id="C_%(table)d_%(code_id)d">%(code)s\n</div>' +
'</td>' +
'</tr>'
)
CODE = '''<span class="%(class)s" style="background-color: %(highlight)s; color: %(color)s;">%(content)s</span>'''
ANNOTATION_CODE = '''<span style="background-color: %(highlight)s;"><a href="javascript:void();" class="annotation"><span class="%(class)s annotation" style="color: %(color)s;">%(content)s</span></a></span>'''
TABLE_END = '''</table>'''
ROW_START = '''<tr><td>'''
ROW_END = '''</td></tr>'''
DIVIDER = '''<span style="color: %(color)s">\n...\n\n</span>'''
ANNOTATION_TBL_START = (
'<div id="comment_list" style="display:none"><div id="comment_wrapper">' +
'<table id="comment_table">' +
'<tr><th>Line/Col</th><th>Comments' +
'<a href="javascript:void(0)" class="table_close" onclick="toggle_annotations();return false;">(close)</a>'
'</th></tr>'
)
ANNOTATION_TBL_END = '''</table></div></div>'''
ANNOTATION_ROW = (
'<tr>' +
'<td class="annotation_link">' +
'<a href="javascript:void(0)" onclick="scroll_to_line(\'C_%(table)d_%(row)d\');return false;">%(link)s</a>' +
'</td>' +
'<td class="annotation_comment"><div class="annotation_comment">%(comment)s</div></td>' +
'<tr>'
)
ANNOTATION_FOOTER = (
'<tr><td colspan=2>' +
'<div class="table_footer"><label>Position </label>' +
'<select id="dock" size="1" onchange="dock_table();">' +
'<option value="0" selected="selected">center</option>' +
'<option value="1">top</option>' +
'<option value="2">bottom</option>' +
'<option value="3">left</option>' +
'<option value="4">right</option>' +
'<option value="5">top left</option>' +
'<option value="6">top right</option>' +
'<option value="7">bottom left</option>' +
'<option value="8">bottom right</option>' +
'</select>' +
'</div>' +
'</td></tr>'
)
BODY_END = '''</pre>%(toolbar)s\n%(js)s\n</body>\n</html>\n'''
INCLUDE_THEME = \
'''
<script type="text/javascript">
%(jscode)s
plist.color_scheme = %(theme)s;
function dump_theme() {
extract_theme('%(name)s');
}
</script>
'''
TOGGLE_LINE_OPTIONS = \
'''
<script type="text/javascript">
%(jscode)s
page_line_info.wrap = false;
page_line_info.ranges = [%(ranges)s];
page_line_info.wrap_size = %(wrap_size)d;
page_line_info.tables = %(tables)s;
page_line_info.header = %(header)s;
page_line_info.gutter = %(gutter)s;
</script>
'''
AUTO_PRINT = \
'''
<script type="text/javascript">
document.getElementsByTagName('body')[0].onload = function (e) { page_print(); self.onload = null; };
</script>
'''
WRAP = \
'''
<script type="text/javascript">
toggle_wrapping();
</script>
'''
HTML_JS_WRAP = \
'''
<script type="text/javascript">
%(jscode)s
</script>
'''
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(" ", '&nbsp;'),
"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 = {
'&': '&amp;',
'>': '&gt;',
'<': '&lt;',
'\t': ' ' * self.tab_size,
'\n': ''
}
return re.sub(
r'(?!\s($|\S))\s',
'&nbsp;',
''.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 = '&nbsp;'
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)