Files
ChocolateyPackages/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportBbcode.py

353 lines
13 KiB
Python

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)