Sublime Forum

Copy text with highlighted colors

#1

Hi, I know this is probably pretty perverse, but is there some way to copy text out of sublime including the highlighted colors?

My motivation is that I’d like to paste code samples into HTML mail or rich text editor with the highlighting for clarity and readability…

Thanks for any hints.

0 Likes

Copy/save with syntax highlighting format
#2

Yeah I need the same as they are handy for presentations.

Guess I’ll have to port github.com/drnic/copy-as-rtf-tmbundle - so it’ll be mac only…

0 Likes

#3

That’d do :smile: Although platform independent version would be even better…

0 Likes

#4

Perhaps using the pygments library would work cross platform:

bitbucket.org/asmodai/pygments-plugin/src

Unfortunately, I cant get that plugin to work - any ideas?

0 Likes

#5

I would very much like to see this functionality.

See Copy/save with syntax highlighting format (in Tech Support) for related information.

0 Likes

#6

I’m interested in this topic, although I’m not sure how far I might pursue it at this stage. There are a couple of available options currently:

Copy the text to another editor, which has a print - or print/save to HTML - option. Within this other editor, chose a theme which is similar to your chosen ST one;
There is an (online or downloadable) pigmentizer tool. This would need a lot of customizing to match your chosen theme;
There is a Pygmentize plugin for ST, which presumably enables the previous tool from within ST;
There is a copy-as-rtf bundle, but this is for TextMate and might be tricky to port. In particular, it isn’t written in Python (Perl?).

I welcome comments on the following approach:
Parse the attached theme (as XML or JSON) to create a dictionary of scopes and colours;
Read the chosen font, and perhaps other ST settings - such as line-padding;
Parse the document using scope_name, extract_scope, substr to create HTML DIVs, with the scope name used as class-names;
Create and attach, or insert, a stylesheet created from the dictionary obtained in step one.

The main obstacle I foresee is that the scopes extracted from the theme file won’t precisely match the scope obtained from ‘extract_scope’. So it might involve some complicated use of ‘score_selector’. And I would need to handle line breaks and tabs, and use html entities for certain characters.

I welcome suggestions, opinions on this topic. Andy.

0 Likes

#7

Well I’m making some progress with this :wink: ; the attached screenshot is an HTML document generated from a keyboard combination from within ST.

But, as you can see, I’m losing tabs and newlines. I’m using ‘scope_name’ to extend a region which contains the same scope, and then ‘substr’ to grab the regions text. How can I retain the tabs and newlines? :question:

I also need to retrieve the background and foreground colours (from the theme file) and escape HTML entities, but I know how to do this bit :smile:

[code]import sublime, sublime_plugin
from xml.dom import minidom

class PrintHtmlCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.colours = {}
self.the_content = ]
path_packages = sublime.packages_path()
settings = sublime.load_settings(‘Preferences.sublime-settings’)
colour_scheme = settings.get(‘color_scheme’)
font_size = settings.get(‘font_size’) or 10
font_face = settings.get(‘font_face’) or ‘Consolas’
# print locals()
# print path_packages # need to remove ‘Packages’
doc = minidom.parse(path_packages + ‘\User\Andy.tmTheme’) # use os.path.sep
the_dict = doc.getElementsByTagName(‘dict’)[0]
the_array = the_dict.getElementsByTagName(‘array’)[0]
dict_items = the_array.getElementsByTagName(‘dict’)[1:]
for item in dict_items:
scope = ‘’; colour = ‘’
for key_tag in item.getElementsByTagName(‘key’):
try:
if key_tag.firstChild.data.strip() == ‘scope’:
scope = key_tag.nextSibling.nextSibling.firstChild.data.strip()
elif key_tag.firstChild.data.strip() == ‘foreground’:
colour = key_tag.nextSibling.nextSibling.firstChild.data.strip()
except:
pass
if scope != ‘’ and colour != ‘’:
self.colours[scope] = colour
curr_view = self.view
size = curr_view.size()
pt = 0; end = 1
while end <= size:
scope_name = curr_view.scope_name(pt)
while curr_view.scope_name(end) == scope_name and end <= size:
end += 1
region = sublime.Region(pt, end)
the_key = curr_view.scope_name(pt).strip()
if self.colours.has_key(the_key):
the_colour = self.colours[the_key]
else:
best_match = -1
for key in self.colours:
if curr_view.score_selector(pt, key) > best_match:
best_match = curr_view.score_selector(pt, key)
the_colour = self.colours[key]
self.colours[the_key] = the_colour
self.the_content.append((curr_view.substr(region), curr_view.scope_name(pt),
the_colour))
pt = end
end = pt + 1
the_html = open(‘test.html’, ‘w’) # defaults to Packages\User currently
the_html.write(’\n\n<style type=“text/css”>\n’)
the_html.write(’\tdiv { display: inline-block; }\n’)
the_html.write(’\n\n\n’)

	for divs in self.the_content:
		the_html.write('<div style=\"color:' + divs[2] + '\">')
		the_html.write(divs[0] + '</div>\n')
	the_html.write('</body>\n</html>')
	the_html.close()[/code]

BTW The above code won’t run for you currently, unless you manually change the theme-filename within the code.

0 Likes

#8

Actually, no worries :wink: (for the minute). substr is capturing the newlines and tabs, but I need to convert them to
and ’ ’ (four spaces) within the HTML.

0 Likes

#9

Making progress :wink: - see screenshot. (Although you may not like my colours!)

0 Likes

#10

Below is a working version if anyone would like to give it a try :wink: . There are two screenshots below to compare the ST and HTML version.

Windows only at the moment!
Assign it a key-binding and it will create an HTML file version of the current view/file;
The HTML will be saved in the same folder as ‘yourfilename_parsed.html’;
It works with tabs or four spaces at the moment. I should be able to modify it to read, and adjust to, your preference for this setting;
I haven’t tried it with any HUGE files yet - it would probably need re-drafting to write each line as it parses the text and, sensibly, to work in a different thread.

[code]import sublime, sublime_plugin
from xml.dom import minidom
import re
from os import path

class PrintHtmlCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.colours = {}
self.the_content = ]
path_packages = sublime.packages_path()
settings = sublime.load_settings(‘Preferences.sublime-settings’)
colour_scheme = settings.get(‘color_scheme’)
colour_scheme = colour_scheme.replace(’/’, ‘\\’)
colour_scheme = colour_scheme.replace(‘Packages’, ‘’)
font_size = settings.get(‘font_size’) or 10
font_face = settings.get(‘font_face’) or ‘Consolas’
doc = minidom.parse(path_packages + colour_scheme)
the_dict = doc.getElementsByTagName(‘dict’)[0]
the_array = the_dict.getElementsByTagName(‘array’)[0]
colour_settings = the_array.getElementsByTagName(‘dict’)[0]
bground = ‘’; fground = ‘’
for key_tag in colour_settings.getElementsByTagName(‘key’):
try:
if key_tag.firstChild.data.strip() == ‘background’:
bground = key_tag.nextSibling.nextSibling.firstChild.data.strip()
elif key_tag.firstChild.data.strip() == ‘foreground’:
fground = key_tag.nextSibling.nextSibling.firstChild.data.strip()
except:
pass
dict_items = the_array.getElementsByTagName(‘dict’)[1:]
for item in dict_items:
scope = ‘’; colour = ‘’
for key_tag in item.getElementsByTagName(‘key’):
try:
if key_tag.firstChild.data.strip() == ‘scope’:
scope = key_tag.nextSibling.nextSibling.firstChild.data.strip()
elif key_tag.firstChild.data.strip() == ‘foreground’:
colour = key_tag.nextSibling.nextSibling.firstChild.data.strip()
except:
pass
if scope != ‘’ and colour != ‘’:
self.colours[scope] = colour
curr_view = self.view
size = curr_view.size()
pt = 0; end = 1
while end <= size:
scope_name = curr_view.scope_name(pt)
while curr_view.scope_name(end) == scope_name and end <= size:
end += 1
region = sublime.Region(pt, end)
the_key = curr_view.scope_name(pt).strip()
if self.colours.has_key(the_key):
the_colour = self.colours[the_key]
else:
if re.match(‘source.[a-zA-Z_]*$’, the_key) is not None:
self.colours[the_key] = fground
the_colour = fground
else:
best_match = -1
for key in self.colours:
if curr_view.score_selector(pt, key) > best_match:
best_match = curr_view.score_selector(pt, key)
the_colour = self.colours[key]
self.colours[the_key] = the_colour
tidied_text = curr_view.substr(region)
tidied_text = tidied_text.replace(’&’, ‘&’)
tidied_text = tidied_text.replace(’<’, ‘<’)
tidied_text = tidied_text.replace(’>’, ‘>’)
tidied_text = tidied_text.replace(’\t’, ’ ’ * 4)
tidied_text = tidied_text.replace(’ ‘, ’ ’ * 4)
tidied_text = tidied_text.replace(’\n’, ‘
’)

		self.the_content.append((tidied_text, curr_view.scope_name(pt), the_colour))
		pt = end
		end = pt + 1
	curr_file = curr_view.file_name()
	head, tail = path.split(curr_file)
	fname, ext = path.splitext(tail)
	print head, tail, fname
	the_html = open(head + '\\' + fname + '_parsed.html', 'w')			# defaults to Packages\User currently
	the_html.write('<html>\n<head>\n<style type=\"text/css\">\n')
	the_html.write('\tspan { display: inline; border: 0; margin: 0; padding: 0; }\n')
	the_html.write('\tbody { ')
	if fground != '': the_html.write('color: ' + fground + ';')
	if bground != '': the_html.write(' background-color: ' + bground + ';')
	the_html.write(' font: ' + `font_size` + 'pt \"' + font_face + '\", Consolas, \"Times New Roman\";')
	the_html.write('\n}\n')
	the_html.write('</style>\n</head>\n<body>\n')

	for spans in self.the_content:
		the_html.write('<span style=\"color:' + spans[2] + '\">')
		the_html.write(spans[0] + '</span>')
	the_html.write('</body>\n</html>')
	the_html.close()[/code]

I should put it on GitHub at some point, but it’s still at an early stage.

Things for me to consider:

  1. Parsing the theme file once and persisting a dictionary of colour values.
  2. If I, instead, code to create a separate stylesheet, and use (a version of) the scopes as class-names, then it would be possible for you to tweak the stylesheet. (A biggish job :wink: )
  3. Perhaps giving an opportunity to change the name and location of the generated file. (Don’t think this is necessary at this stage.)
  4. Adapt for Apple, linux - probably need a little guidance with this;
  5. More error handling.



0 Likes

#11

To make this work for other oses I just need to change the few lines indicated. Could someone with Apple or Linux offer guidance :wink: . I know I can use ‘os.path.sep’ but it’s the escaping part that I need to be careful with. Andy.

[code] path_packages = sublime.packages_path()
settings = sublime.load_settings(‘Preferences.sublime-settings’)
colour_scheme = settings.get(‘color_scheme’)
colour_scheme = colour_scheme.replace(’/’, ‘\\’) # this one
colour_scheme = colour_scheme.replace(‘Packages’, ‘’)
font_size = settings.get(‘font_size’) or 10
font_face = settings.get(‘font_face’) or ‘Consolas’
tab_size = settings.get(‘tab_size’) or 4
doc = minidom.parse(path_packages + colour_scheme) # this one?

	curr_file = curr_view.file_name()
	head, tail = path.split(curr_file)
	fname, ext = path.splitext(tail)
	print head, tail, fname
	the_html = open(head + '\\' + fname + '_parsed.html', 'w')		# this one[/code]
0 Likes

#12

There’s a slight update here: it reads your chosen **tab_size **and uses this value to space out the resultant html.

[code]import sublime, sublime_plugin
from xml.dom import minidom
import re
from os import path

class PrintHtmlCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.colours = {}
self.the_content = ]
path_packages = sublime.packages_path()
settings = sublime.load_settings(‘Preferences.sublime-settings’)
colour_scheme = settings.get(‘color_scheme’)
colour_scheme = colour_scheme.replace(’/’, ‘\\’)
colour_scheme = colour_scheme.replace(‘Packages’, ‘’)
font_size = settings.get(‘font_size’) or 10
font_face = settings.get(‘font_face’) or ‘Consolas’
tab_size = settings.get(‘tab_size’) or 4
doc = minidom.parse(path_packages + colour_scheme)
the_dict = doc.getElementsByTagName(‘dict’)[0]
the_array = the_dict.getElementsByTagName(‘array’)[0]
colour_settings = the_array.getElementsByTagName(‘dict’)[0]
bground = ‘’; fground = ‘’
for key_tag in colour_settings.getElementsByTagName(‘key’):
try:
if key_tag.firstChild.data.strip() == ‘background’:
bground = key_tag.nextSibling.nextSibling.firstChild.data.strip()
elif key_tag.firstChild.data.strip() == ‘foreground’:
fground = key_tag.nextSibling.nextSibling.firstChild.data.strip()
except:
pass
dict_items = the_array.getElementsByTagName(‘dict’)[1:]
for item in dict_items:
scope = ‘’; colour = ‘’
for key_tag in item.getElementsByTagName(‘key’):
try:
if key_tag.firstChild.data.strip() == ‘scope’:
scope = key_tag.nextSibling.nextSibling.firstChild.data.strip()
elif key_tag.firstChild.data.strip() == ‘foreground’:
colour = key_tag.nextSibling.nextSibling.firstChild.data.strip()
except:
pass
if scope != ‘’ and colour != ‘’:
self.colours[scope] = colour
curr_view = self.view
size = curr_view.size()
pt = 0; end = 1
while end <= size:
scope_name = curr_view.scope_name(pt)
while curr_view.scope_name(end) == scope_name and end <= size:
end += 1
region = sublime.Region(pt, end)
the_key = curr_view.scope_name(pt).strip()
if self.colours.has_key(the_key):
the_colour = self.colours[the_key]
else:
if re.match(‘source.[a-zA-Z_]*$’, the_key) is not None:
self.colours[the_key] = fground
the_colour = fground
else:
best_match = -1
for key in self.colours:
if curr_view.score_selector(pt, key) > best_match:
best_match = curr_view.score_selector(pt, key)
the_colour = self.colours[key]
self.colours[the_key] = the_colour
tidied_text = curr_view.substr(region)
tidied_text = tidied_text.replace(’&’, ‘&’)
tidied_text = tidied_text.replace(’<’, ‘<’)
tidied_text = tidied_text.replace(’>’, ‘>’)
tidied_text = tidied_text.replace(’\t’, ’ ’ * tab_size)
tidied_text = tidied_text.replace(’ ’ * tab_size, ’ ’ * tab_size)
tidied_text = tidied_text.replace(’\n’, ‘
’)

		self.the_content.append((tidied_text, curr_view.scope_name(pt), the_colour))
		pt = end
		end = pt + 1
	curr_file = curr_view.file_name()
	head, tail = path.split(curr_file)
	fname, ext = path.splitext(tail)
	print head, tail, fname
	the_html = open(head + '\\' + fname + '_parsed.html', 'w')			# defaults to Packages\User currently
	the_html.write('<html>\n<head>\n<style type=\"text/css\">\n')
	the_html.write('\tspan { display: inline; border: 0; margin: 0; padding: 0; }\n')
	the_html.write('\tbody { ')
	if fground != '': the_html.write('color: ' + fground + ';')
	if bground != '': the_html.write(' background-color: ' + bground + ';')
	the_html.write(' font: ' + `font_size` + 'pt \"' + font_face + '\", Consolas, \"Times New Roman\";')
	the_html.write('\n}\n')
	the_html.write('</style>\n</head>\n<body>\n')

	for spans in self.the_content:
		the_html.write('<span style=\"color:' + spans[2] + '\">')
		the_html.write(spans[0] + '</span>')
	the_html.write('</body>\n</html>')
	the_html.close()[/code]
0 Likes

#13

This third (final… for the moment!) version should, I believe, work in other oses - but I’m unable to test it.

It’s more memory-efficient, as it writes each line to the file as it progresses, rather than building a potentially huge list (before writing to the file).

It saves the original file extension as part of the new name. So ‘this.js’ becomes ‘this_js_parsed.html’. I believe this to be useful, as it’s common to have different file types having the same filename.

[code]import sublime, sublime_plugin
from xml.dom import minidom
import re
from os import path

class PrintHtmlCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.colours = {}
path_packages = sublime.packages_path()
settings = sublime.load_settings(‘Preferences.sublime-settings’)
colour_scheme = settings.get(‘color_scheme’)
colour_scheme = colour_scheme.replace(’/’, ‘\\’)
colour_scheme = colour_scheme.replace(‘Packages’, ‘’)
font_size = settings.get(‘font_size’) or 10
font_face = settings.get(‘font_face’) or ‘Consolas’
tab_size = settings.get(‘tab_size’) or 4
# padd_bottom = settings.get(‘line_padding_bottom’) or 0
doc = minidom.parse(path_packages + colour_scheme)
the_dict = doc.getElementsByTagName(‘dict’)[0]
the_array = the_dict.getElementsByTagName(‘array’)[0]
colour_settings = the_array.getElementsByTagName(‘dict’)[0]
bground = ‘’; fground = ‘’
for key_tag in colour_settings.getElementsByTagName(‘key’):
try:
if key_tag.firstChild.data.strip() == ‘background’:
bground = key_tag.nextSibling.nextSibling.firstChild.data.strip()
elif key_tag.firstChild.data.strip() == ‘foreground’:
fground = key_tag.nextSibling.nextSibling.firstChild.data.strip()
except:
pass
dict_items = the_array.getElementsByTagName(‘dict’)[1:]
for item in dict_items:
scope = ‘’; colour = ‘’
for key_tag in item.getElementsByTagName(‘key’):
try:
if key_tag.firstChild.data.strip() == ‘scope’:
scope = key_tag.nextSibling.nextSibling.firstChild.data.strip()
elif key_tag.firstChild.data.strip() == ‘foreground’:
colour = key_tag.nextSibling.nextSibling.firstChild.data.strip()
except:
pass
if scope != ‘’ and colour != ‘’:
self.colours[scope] = colour

	curr_view = self.view
	curr_file = curr_view.file_name()
	head, tail = path.split(curr_file)
	fname, ext = path.splitext(tail)
	ext = ext.replace('.', '_')
	the_html = open(head + path.sep + fname + ext + '_parsed.html', 'w')
	the_html.write('<html>\n<head>\n<style type=\"text/css\">\n')
	the_html.write('\tspan { display: inline; border: 0; margin: 0; padding: 0; }\n')
	the_html.write('\tbody { ')
	if fground != '': the_html.write('color: ' + fground + ';')
	if bground != '': the_html.write(' background-color: ' + bground + ';')
	the_html.write(' font: ' + `font_size` + 'pt \"' + font_face + '\", Consolas, Monospace;')
	the_html.write('\n}\n')
	the_html.write('</style>\n</head>\n<body>\n')

	size = curr_view.size()
	pt = 0; end = 1
	while end <= size:
		scope_name = curr_view.scope_name(pt)
		while curr_view.scope_name(end) == scope_name and end <= size:
			end += 1
		region = sublime.Region(pt, end)
		the_key = scope_name.strip()
		if self.colours.has_key(the_key):
			the_colour = self.colours[the_key]
		else:
			if re.match('source\.[a-zA-Z_]*$', the_key) is not None:
				self.colours[the_key] = fground
				the_colour = fground
			else:
				best_match = -1
				for key in self.colours:
					if curr_view.score_selector(pt, key) > best_match:
						best_match = curr_view.score_selector(pt, key)
						the_colour = self.colours[key]
				self.colours[the_key] = the_colour
		tidied_text = curr_view.substr(region)
		tidied_text = tidied_text.replace('&', '&amp;')
		tidied_text = tidied_text.replace('<', '&lt;')
		tidied_text = tidied_text.replace('>', '&gt;')
		tidied_text = tidied_text.replace('\t', '&nbsp;' * tab_size)
		tidied_text = tidied_text.replace(' ' * tab_size, '&nbsp;' * tab_size)
		tidied_text = tidied_text.replace('\n', '<br>')

		the_html.write('<span style=\"color:' + the_colour + '\">')
		the_html.write(tidied_text + '</span>')
		pt = end
		end = pt + 1
	the_html.write('</body>\n</html>')
	the_html.close()[/code]

I was considering applying your chosen ‘line_padding_top/bottom’, but this requires quite a bit of css manipulation.

What’s the default font for Apple and Linux, etc.? I’ve left it currently as: ‘“Your font”, Consolas, Monospace’.

I’m also considering a version which would retain/generate the line-numbers, but I’ve had no feedback yet. Andy.

0 Likes

#14

I tried it out on Mac, and it worked great (after I fixed some things). It grabbed my theme colors just fine.

Don’t do this:

colour_scheme = colour_scheme.replace('/', '\\\\')

That is only valid for windows, use something like:

colour_scheme = os.path.normpath(colour_scheme)

It is neat that it can convert the whole file, but I would prefer under the selection. This also worked quite well once I changed the code to this. Granted it probably needs a check if there are no sels, but I was just testing.

size = curr_view.sel()[0].end() pt = curr_view.sel()[0].begin(); end = pt

Very impressive, and well done. Get this on Package Control :smile:. I suspect this could work on Linux, but I imagine it will require some tweaking to make sure you are importing the libraries you need.

0 Likes

#15

Hi @facelessuser and thank you.

colour_scheme = colour_scheme.replace('/', '\\\\')

I figured this would be ignored for non-Windows, but I should check ‘normpath’ as you suggest.

I figured it would be easy to amend to work with the selection - similar to your code. I could first check whether the selection is empty(). I could also check the column number and, perhaps, precede the HTML with the same number of spaces. I was thinking I could append a number to the new file name; this way, we could highlight different areas and quickly save them as separate files. Might this be useful? I might leave this decision till a bit later.

I studied three different xml modules :open_mouth: before settling on ‘minidom’ - in particular because it shouldn’t require a separate install. But I don’t know about Linux: perhaps someone with Linux might contribute :wink:

I might look at creating a separate thread (for my own education :smile: ). But I expect it should work well even with large files. (It does copy the whole theme file initially, but this is typically between 4-800 lines.)

I shall get it on my GitHub and look at Package Control after that. Regards, Andy.

0 Likes

#16

If you select more than four characters then this version will create an HTML file based on the selection, named ‘_part.html’. Otherwise, it is based on the whole file and named ‘_parsed.html’.

It should work for other oses but I need some feedback from Linux, Mac users. Andy.

[code]import sublime, sublime_plugin
from xml.dom import minidom
import re
from os import path

class PrintHtmlCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.colours = {}
path_packages = sublime.packages_path()
settings = sublime.load_settings(‘Preferences.sublime-settings’)
colour_scheme = settings.get(‘color_scheme’)
# colour_scheme = colour_scheme.replace(’/’, ‘\\’)
colour_scheme = path.normpath(colour_scheme)
colour_scheme = colour_scheme.replace(‘Packages’, ‘’)
font_size = settings.get(‘font_size’) or 10
font_face = settings.get(‘font_face’) or ‘Consolas’
tab_size = settings.get(‘tab_size’) or 4
# padd_bottom = settings.get(‘line_padding_bottom’) or 0
doc = minidom.parse(path_packages + colour_scheme)
the_dict = doc.getElementsByTagName(‘dict’)[0]
the_array = the_dict.getElementsByTagName(‘array’)[0]
colour_settings = the_array.getElementsByTagName(‘dict’)[0]
bground = ‘’; fground = ‘’
for key_tag in colour_settings.getElementsByTagName(‘key’):
try:
if key_tag.firstChild.data.strip() == ‘background’:
bground = key_tag.nextSibling.nextSibling.firstChild.data.strip()
elif key_tag.firstChild.data.strip() == ‘foreground’:
fground = key_tag.nextSibling.nextSibling.firstChild.data.strip()
except:
pass
dict_items = the_array.getElementsByTagName(‘dict’)[1:]
for item in dict_items:
scope = ‘’; colour = ‘’
for key_tag in item.getElementsByTagName(‘key’):
try:
if key_tag.firstChild.data.strip() == ‘scope’:
scope = key_tag.nextSibling.nextSibling.firstChild.data.strip()
elif key_tag.firstChild.data.strip() == ‘foreground’:
colour = key_tag.nextSibling.nextSibling.firstChild.data.strip()
except:
pass
if scope != ‘’ and colour != ‘’:
self.colours[scope] = colour

	curr_view = self.view
	curr_file = curr_view.file_name()
	head, tail = path.split(curr_file)
	fname, ext = path.splitext(tail)
	ext = ext.replace('.', '_')
	
	curr_sel = curr_view.sel()[0]
	if curr_sel.empty() or abs(curr_sel.end() - curr_sel.begin()) < 4:
		the_html = open(head + path.sep + fname + ext + '_parsed.html', 'w')
		size = curr_view.size()
		pt = 0; end = 1
	else:
		the_html = open(head + path.sep + fname + ext + '_part.html', 'w')
		size = curr_sel.end()
		pt = curr_sel.begin()
		end = pt + 1
	the_html.write('<html>\n<head>\n<style type=\"text/css\">\n')
	the_html.write('\tspan { display: inline; border: 0; margin: 0; padding: 0; }\n')
	the_html.write('\tbody { ')
	if fground != '': the_html.write('color: ' + fground + ';')
	if bground != '': the_html.write(' background-color: ' + bground + ';')
	the_html.write(' font: ' + `font_size` + 'pt \"' + font_face + '\", Consolas, Monospace;')
	the_html.write('\n}\n')
	the_html.write('</style>\n</head>\n<body>\n')
	
	while end <= size:
		scope_name = curr_view.scope_name(pt)
		while curr_view.scope_name(end) == scope_name and end <= size:
			end += 1
		region = sublime.Region(pt, end)
		the_key = scope_name.strip()
		if self.colours.has_key(the_key):
			the_colour = self.colours[the_key]
		else:
			if re.match('source\.[a-zA-Z_]*$', the_key) is not None:
				self.colours[the_key] = fground
				the_colour = fground
			else:
				best_match = -1
				for key in self.colours:
					if curr_view.score_selector(pt, key) > best_match:
						best_match = curr_view.score_selector(pt, key)
						the_colour = self.colours[key]
				self.colours[the_key] = the_colour
		tidied_text = curr_view.substr(region)
		tidied_text = tidied_text.replace('&', '&amp;')
		tidied_text = tidied_text.replace('<', '&lt;')
		tidied_text = tidied_text.replace('>', '&gt;')
		tidied_text = tidied_text.replace('\t', '&nbsp;' * tab_size)
		tidied_text = tidied_text.replace(' ' * tab_size, '&nbsp;' * tab_size)
		tidied_text = tidied_text.replace('\n', '<br>')

		the_html.write('<span style=\"color:' + the_colour + '\">')
		the_html.write(tidied_text + '</span>')
		pt = end
		end = pt + 1
	the_html.write('</body>\n</html>')
	the_html.close()[/code]
0 Likes

#17

Well it’s on GitHub now :smile:

You can trying pulling, pushing or forking, but I’ve haven’t read into this subject yet so it might take me a while to respond :laughing:

I know how to (just about) update my file and add new files to my repo. And, if I receive a request, I can probably ‘click through’ to respond to it.

How to add to Package Control… Presumably, this creates a link to my repo and is able to monitor updates to it. Andy.

0 Likes

#18

[quote=“agibsonsw”]Hi @facelessuser and thank you.

colour_scheme = colour_scheme.replace('/', '\\\\')

I figured this would be ignored for non-Windows, but I should check ‘normpath’ as you suggest.

I figured it would be easy to amend to work with the selection - similar to your code. I could first check whether the selection is empty(). I could also check the column number and, perhaps, precede the HTML with the same number of spaces. I was thinking I could append a number to the new file name; this way, we could highlight different areas and quickly save them as separate files. Might this be useful? I might leave this decision till a bit later.

I studied three different xml modules :open_mouth: before settling on ‘minidom’ - in particular because it shouldn’t require a separate install. But I don’t know about Linux: perhaps someone with Linux might contribute :wink:

I might look at creating a separate thread (for my own education :smile: ). But I expect it should work well even with large files. (It does copy the whole theme file initially, but this is typically between 4-800 lines.)

I shall get it on my GitHub and look at Package Control after that. Regards, Andy.[/quote]

The main linux issue is mainly if there is not a full install of Python 2.6. For instance, in Ubuntu, when you try and import an XML module, it usually fails because it cannot find an expat module. This is because Ubuntu comes with a minimal Python2.6 by default. Usually installing a full Python 2.6 is sufficient to get Linux working.

Sublime Text 2 by default trys adding a number of Python 2.6 Linux paths to the Python system path to ensure default modules can be accessed.
From ST2 console output (I think ST2 is Ubuntu specific though)

Py_GetPath(): :./lib/python26.zip:./lib/python2.6/:./lib/python2.6/plat-linux2:./lib/python2.6/lib-tk:./lib/python2.6/lib-old:./lib/python2.6/lib-dynload

To be extra safe, you could just add maybe a configurable path to try and add to the Python system path if it doesn’t exist. I think that would make it configurable for weird installs on different distros, but hopefully ST2’s attempt would be sufficient if the user has a full install of 2.6. Just something you might need to be aware of when Linux users start getting errors.

0 Likes

#19

[quote=“agibsonsw”]Well it’s on GitHub now :smile:

You can trying pulling, pushing or forking, but I’ve haven’t read into this subject yet so it might take me a while to respond :laughing:

I know how to (just about) update my file and add new files to my repo. And, if I receive a request, I can probably ‘click through’ to respond to it.

How to add to Package Control… Presumably, this creates a link to my repo and is able to monitor updates to it. Andy.[/quote]

Adding to Package Control is easy, you just have to fork @wbond’s default repository channel. You then just edit the respository JSON file and add your github link. You can actually edit it straight from github, so it is very easy.

Thanks for this package, it is pretty nifty and useful.

0 Likes

#20

@facelessuser. Thanks again.

Mmm, yes… I shall look into this :smile:

I shall await feedback from Mr or Mrs Linux. I’m in two minds about this though: if their set-up needs to be tweaked to enable “standard” libraries then surely they need to be aware of this, and configure their system accordingly?!

BTW The following is kinda neat, but there’s no advantage to be gained from the current - more straight-forward - approach:

for x, y in zip(('&', '<', '>', '\t', ' ' * tab_size, '\n'), ('&amp;', '&lt;', '&gt;', '&nbsp;' * tab_size, '&nbsp;' * tab_size, '<br>')): tidied_text = tidied_text.replace(x, y)
I might tinker with a version to add line numbers (li items) :wink: . But I’ll leave the current version as is for the moment.

Andy.

0 Likes