feat(SublimeText2.EditorPackages): cache packages
This commit is contained in:
@@ -0,0 +1,352 @@
|
||||
import sublime
|
||||
import sublime_plugin
|
||||
from os import path
|
||||
import tempfile
|
||||
import sys
|
||||
import re
|
||||
|
||||
PACKAGE_SETTINGS = "ExportHtml.sublime-settings"
|
||||
|
||||
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
|
||||
|
||||
NUMBERED_BBCODE_LINE = '[color=%(color)s]%(line)s [/color]%(code)s\n'
|
||||
|
||||
BBCODE_LINE = '%(code)s\n'
|
||||
|
||||
BBCODE_CODE = '[color=%(color)s]%(content)s[/color]'
|
||||
|
||||
BBCODE_ESCAPE = '[/color][color=%(color_open)s]%(content)s[/color][color=%(color_close)s]'
|
||||
|
||||
BBCODE_BOLD = '[b]%(content)s[/b]'
|
||||
|
||||
BBCODE_ITALIC = '[i]%(content)s[/i]'
|
||||
|
||||
POST_START = '[pre=%(bg_color)s]'
|
||||
|
||||
POST_END = '[/pre]\n'
|
||||
|
||||
BBCODE_MATCH = re.compile(r"""(\[/?)((?:code|pre|table|tr|td|th|b|i|u|sup|color|url|img|list|trac|center|quote|size|li|ul|ol|youtube|gvideo)(?:=[^\]]+)?)(\])""")
|
||||
|
||||
FILTER_MATCH = re.compile(r'^(?:(brightness|saturation|hue|colorize)\((-?[\d]+|[\d]*\.[\d]+)\)|(sepia|grayscale|invert))$')
|
||||
|
||||
|
||||
class ExportBbcodePanelCommand(sublime_plugin.WindowCommand):
|
||||
def execute(self, value):
|
||||
if value >= 0:
|
||||
view = self.window.active_view()
|
||||
if view != None:
|
||||
ExportBbcode(view).run(**self.args[value])
|
||||
|
||||
def run(self):
|
||||
options = sublime.load_settings(PACKAGE_SETTINGS).get("bbcode_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 ExportBbcodeCommand(sublime_plugin.WindowCommand):
|
||||
def run(self, **kwargs):
|
||||
view = self.window.active_view()
|
||||
if view != None:
|
||||
ExportBbcode(view).run(**kwargs)
|
||||
|
||||
|
||||
class ExportBbcode(object):
|
||||
def __init__(self, view):
|
||||
self.view = view
|
||||
|
||||
def process_inputs(self, **kwargs):
|
||||
return {
|
||||
"numbers": bool(kwargs.get("numbers", False)),
|
||||
"color_scheme": kwargs.get("color_scheme", None),
|
||||
"multi_select": bool(kwargs.get("multi_select", False)),
|
||||
"clipboard_copy": bool(kwargs.get("clipboard_copy", True)),
|
||||
"view_open": bool(kwargs.get("view_open", False)),
|
||||
"filter": kwargs.get("filter", "")
|
||||
}
|
||||
|
||||
def setup(self, **kwargs):
|
||||
path_packages = sublime.packages_path()
|
||||
|
||||
# Get get general document preferences from sublime preferences
|
||||
settings = sublime.load_settings('Preferences.sublime-settings')
|
||||
eh_settings = sublime.load_settings(PACKAGE_SETTINGS)
|
||||
self.tab_size = settings.get('tab_size', 4)
|
||||
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.hl_continue = None
|
||||
self.curr_hl = None
|
||||
self.sels = []
|
||||
self.multi_select = self.check_sel() if kwargs["multi_select"] else False
|
||||
self.size = self.view.size()
|
||||
self.pt = 0
|
||||
self.end = 0
|
||||
self.curr_row = 0
|
||||
self.empty_space = None
|
||||
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))
|
||||
|
||||
# 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.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'), simple_strip=True)
|
||||
self.fground = self.strip_transparency(colour_settings.get("foreground", '#000000'))
|
||||
self.gbground = self.bground
|
||||
self.gfground = self.fground
|
||||
|
||||
# Create scope colors mapping from color scheme file
|
||||
self.colours = {self.view.scope_name(self.end).split(' ')[0]: {"color": self.fground, "style": []}}
|
||||
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)
|
||||
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:
|
||||
self.colours[scope] = {"color": self.strip_transparency(colour), "style": style}
|
||||
|
||||
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 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")
|
||||
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 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 guess_colour(self, the_key):
|
||||
the_colour = None
|
||||
the_style = None
|
||||
if the_key in self.colours:
|
||||
the_colour = self.colours[the_key]["color"]
|
||||
the_style = self.colours[the_key]["style"]
|
||||
else:
|
||||
best_match = 0
|
||||
for key in self.colours:
|
||||
if self.view.score_selector(self.pt, key) > best_match:
|
||||
best_match = self.view.score_selector(self.pt, key)
|
||||
the_colour = self.colours[key]["color"]
|
||||
the_style = self.colours[key]["style"]
|
||||
self.colours[the_key] = {"color": the_colour, "style": the_style}
|
||||
return the_colour, the_style
|
||||
|
||||
def print_line(self, line, num):
|
||||
if self.numbers:
|
||||
bbcode_line = NUMBERED_BBCODE_LINE % {
|
||||
"color": self.gfground,
|
||||
"line": str(num).rjust(self.gutter_pad),
|
||||
"code": line
|
||||
}
|
||||
else:
|
||||
bbcode_line = BBCODE_LINE % {"code": line}
|
||||
|
||||
return bbcode_line
|
||||
|
||||
def convert_view_to_bbcode(self, the_bbcode):
|
||||
for line in self.view.split_by_newlines(sublime.Region(self.end, self.size)):
|
||||
self.empty_space = None
|
||||
self.size = line.end()
|
||||
line = self.convert_line_to_bbcode()
|
||||
the_bbcode.write(self.print_line(line, self.curr_row))
|
||||
self.curr_row += 1
|
||||
|
||||
def repl(self, m, the_colour):
|
||||
return m.group(1) + (
|
||||
BBCODE_ESCAPE % {
|
||||
"color_open": the_colour,
|
||||
"color_close": the_colour,
|
||||
"content": m.group(2)
|
||||
}
|
||||
) + m.group(3)
|
||||
|
||||
def format_text(self, line, text, the_colour, the_style):
|
||||
text = text.replace('\t', ' ' * self.tab_size).replace('\n', '')
|
||||
if self.empty_space != None:
|
||||
text = self.empty_space + text
|
||||
self.empty_space = None
|
||||
if text.strip(' ') == '':
|
||||
self.empty_space = text
|
||||
else:
|
||||
code = ""
|
||||
text = BBCODE_MATCH.sub(lambda m: self.repl(m, the_colour), text)
|
||||
bold = False
|
||||
italic = False
|
||||
for s in the_style:
|
||||
if s == "bold":
|
||||
bold = True
|
||||
if s == "italic":
|
||||
italic = True
|
||||
code += (BBCODE_CODE % {"color": the_colour, "content": text})
|
||||
if italic:
|
||||
code = (BBCODE_ITALIC % {"color": the_colour, "content": code})
|
||||
if bold:
|
||||
code = (BBCODE_BOLD % {"color": the_colour, "content": code})
|
||||
line.append(code)
|
||||
|
||||
def convert_line_to_bbcode(self):
|
||||
line = []
|
||||
|
||||
while self.end <= self.size:
|
||||
# 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:
|
||||
self.end += 1
|
||||
the_colour, the_style = self.guess_colour(scope_name)
|
||||
|
||||
region = sublime.Region(self.pt, self.end)
|
||||
# Normal text formatting
|
||||
text = self.view.substr(region)
|
||||
self.format_text(line, text, the_colour, the_style)
|
||||
|
||||
# Continue walking through line
|
||||
self.pt = self.end
|
||||
self.end = self.pt + 1
|
||||
|
||||
# Join line segments
|
||||
return ''.join(line)
|
||||
|
||||
def write_body(self, the_bbcode):
|
||||
the_bbcode.write(POST_START % {"bg_color": self.bground})
|
||||
|
||||
# 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)
|
||||
self.convert_view_to_bbcode(the_bbcode)
|
||||
count += 1
|
||||
|
||||
if count < total:
|
||||
the_bbcode.write("\n" + (BBCODE_CODE % {"color": self.fground, "content": "..."}) + "\n\n")
|
||||
|
||||
else:
|
||||
self.setup_print_block(self.view.sel()[0])
|
||||
self.convert_view_to_bbcode(the_bbcode)
|
||||
|
||||
the_bbcode.write(POST_END)
|
||||
|
||||
def run(self, **kwargs):
|
||||
inputs = self.process_inputs(**kwargs)
|
||||
self.setup(**inputs)
|
||||
|
||||
delete = False if inputs["view_open"] else True
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=delete, suffix='.txt') as the_bbcode:
|
||||
self.write_body(the_bbcode)
|
||||
if inputs["clipboard_copy"]:
|
||||
the_bbcode.seek(0)
|
||||
sublime.set_clipboard(the_bbcode.read())
|
||||
sublime.status_message("Export to BBCode: copied to clipboard")
|
||||
|
||||
if inputs["view_open"]:
|
||||
self.view.window().open_file(the_bbcode.name)
|
||||
Reference in New Issue
Block a user