Sublime Forum

Copy text with highlighted colors

#35

The external module is only for automatic opening of the browser. You would be including the dependencies, and it allows it to work on Linux, Mac, and Windows. I understand your general feeling of not wanting to include a external module, but trying to get support for Linux, Mac, and Windows when someone has already done it, will be a huge headache. That is kind of why these things exist…to make peoples lives easier.

Also, with other packages using this same module, it shows it is effective and stable. I have to be honest, in this case, unless you want to write your own library for interfacing with Windows, Mac, and Linux KDE and Gnome environments, I don’t see why you wouldn’t want to use it.

The temp file stuff on the other hand is built-in.

Side note: There does seem to be some issues with some languages guessing scopes. C++ particularly has most of the text colored as comments. The guessing algorithm probably will need some work for better guessing.

0 Likes

#36

This seems to guess colors better in C++ etc and not assign comment colors to everything.

default_scope = curr_view.scope_name(end).split(' ')[0] 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 the_colour = fground 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] if curr_view.score_selector(pt, default_scope) > best_match: the_colour = fground self.colours[the_key] = the_colour

0 Likes

#37

[quote=“facelessuser”]This seems to guess colors better in C++ etc and not assign comment colors to everything.

default_scope = curr_view.scope_name(end).split(' ')[0] 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 the_colour = fground 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] if curr_view.score_selector(pt, default_scope) > best_match: the_colour = fground self.colours[the_key] = the_colour[/quote]

Can you describe briefly the change(s) you’ve made - save me some time :smiley:

0 Likes

#38

[quote=“agibsonsw”]

[quote=“facelessuser”]This seems to guess colors better in C++ etc and not assign comment colors to everything.

default_scope = curr_view.scope_name(end).split(' ')[0] 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 the_colour = fground 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] if curr_view.score_selector(pt, default_scope) > best_match: the_colour = fground self.colours[the_key] = the_colour[/quote]

Can you describe briefly the change(s) you’ve made - save me some time :smiley:[/quote]

The source scope is always the first scope.

souce.language otherscope.etc.etc.

So, I store the the default scope. And after you did your best_match guessing, I compare the scope against the default. If the score is greater for the default scope than it is for the guessed scope, use the default scope instead (fground color).

0 Likes

#39

@facelessuser TQ. I shall examine/incorporate this. I want to look at it in a little more detail, to discover the cause. (Presumably ‘comment’ is the first colour in your theme?)

I adopted you’re “start from…” line numbering suggestion, but only when printing a selection. That is, I want the line numbering to agree with the code. Andy.

0 Likes

#40

Oh, one more thing. I think this is the last.

This destroys my alignment in code.

tidied_text = tidied_text.replace(' ' * tab_size, '&nbsp;' * tab_size)

Please use this instead for spaces (you can still convert tabs, but spaces is bad).

tidied_text = tidied_text.replace(' ' , '&nbsp;' )
0 Likes

#41

@facelessuser: No problem. That replaces all spaces - I might try to pursued it to only apply at the beginning of the line, otherwise it seems like overkill.

I do :wink: take your comments on board about the desktop library and non-Windows users. But I’ll explore the C++ (and similar) issue(s) first.

I’ve updated my repository with recent changes (not including the spaces, yet, or the C++ changes).

0 Likes

#42

[quote=“agibsonsw”]@facelessuser: No problem. That replaces all spaces - I might try to pursued it to only apply at the beginning of the line, otherwise it seems like overkill.

I do :wink: take your comments on board about the desktop library and non-Windows users. But I’ll explore the C++ (and similar) issue(s) first.

I’ve updated my repository with recent changes (not including the spaces, yet, or the C++ changes).[/quote]

Thanks. I hope I am not coming off two aggressive or anything. I think this plugin is really cool, so I am just trying to give straight forward and honest feedback.

I do realize not everything I may suggest will be taken up, but I think it is better to throw it out there anyways. Ultimately it is your plugin, so you make the calls. People are always going to throw there 2 cents in (like me :smile: ). The fact that I am so vocal just shows that I really like the plugin and intend to use it.

0 Likes

#43

@facelessuser: Hey, no problem at all! I welcome your comments and assistance :wink:

I intend to understand, and agree with (mostly…) any changes made to the code. So I’ll learn about pulling, forking, bundling other libraries, etc., along the way.

I only started this yesterday out of interest, but if it proves useful as a plug-in then that’s great. In the meantime, I’ve learnt about, coded (and discarded!) three different xml parsers.

(Originally I considered creating an .rtf, or using COM automation to format the text in Word - glad I took the HTML road :laughing: )

Keep the commentary coming! Andy.

0 Likes

#44

I haven’t applied your changes yet, but my C++ sample is formatting source.c++ and other scopes with a ‘random’ scope way down in the file. Maybe it’s the pluses ++ that are causing the issue. Are there any other languages you’ve tested?

Added: C# seems to be okay, even coping with four spaces(?). Do you use four spaces…?

0 Likes

#45

@facelessuser

I think you’re right :wink: I’ll replace all spaces with   - it’ll be more robust in terms of ‘mono-spacing’ the code.

I think I’ll also have to wrap the lines within li’s, even without the line numbers’ option. Then I can apply css margins to create paragraph spacing - hopefully avoiding collapsing margins, he, he!

0 Likes

#46

[quote=“agibsonsw”]I haven’t applied your changes yet, but my C++ sample is formatting source.c++ and other scopes with a ‘random’ scope way down in the file. Maybe it’s the pluses ++ that are causing the issue. Are there any other languages you’ve tested?

Added: C# seems to be okay, even coping with four spaces(?). Do you use four spaces…?[/quote]

JSON also turned brackets into comments. I assume there are many more, but after applying my changes, they all went away. I would need time to try more languages.

0 Likes

#47

I’ve now applied (some/most of) your changes, and also changed the default **best_match **to 0 :laughing:. I’ll have to do a bit of testing as well. (Need to discover whether it’s the plusses, something about the theme file, or other oddities in the scopes.)

I’ve now put everything into li’s, so that I can adjust the paragraph spacing (margins) using the users’ line-padding settings. When not using line numbers I have a few disappearing, blank lines - collapsing margins - as I guessed :smile:. But I think I just need to put insert   if there happens to be a *completely *empty span.

0 Likes

#48

It has nothing to do with the “++”. If I have simple C code and I set the language to C instead of C++, so source now equals “source.c” all my text is still comments. Yours might be different if your theme is different, but yes it is the first entry in the theme. It never finds anything better (they are all equally bad matches). But “source.c” is a much better match than comments, which is why I compare it. And this same flaw bleeds into other languages like JSON etc. Some languages, like Python, do not exhibit this issue due to the way the items are scoped.

0 Likes

#49

Also, this is really a show stopper for me:

tidied_text = tidied_text.replace(' ' * tab_size, '&nbsp;' * tab_size)

Since this only replaces a tabsize * spaces to tabsize * ’ ', it messes up alignment of structures:

You can run this through and see how alignment gets messed up. Tab set to 4. Now with larger structures, you will see this getting real ugly.

const s_SomeType c_SomeClass::SomeList] = { { SOME_ITEM, 3 }, { SOME_OTHER_ITEM, 4 }, END_OF_LIST() }

It really shouldn’t be trying to correct spaces, it should be trying to give the closes representation to what is in the editor. Converting a tab to spaces makes sense.

0 Likes

#50

@facelessuser: Is the problem still present after your changes?

A sensible, alternative approach might be to set the first dictionary entry to ‘source.lang’: ‘foreground-colour’? This is how ST behaves anyway, even though it’s not a visible rule in the theme. The problem might disappear then :smile:

Different topic: I’m ending up with lots of (completely) empty spans in the code:

<li><span style="color:#FFCC11"></span></li>
This is not necessarily an issue. However, having converted everything to fit within li’s - to enable some paragraph spacing - should an a li contain just an empty span, then the line collapses. I’m not sure whether these empty spans were always there, or have arisen recently. I’ll have to try and overcome this because, I believe, the li’s are necessary.

Aha! If the tidied text is just ‘\n’ the len-gth is this is two! This should lead me to a solution. Andy.

0 Likes

#51

[quote=“facelessuser”]Also, this is really a show stopper for me:

tidied_text = tidied_text.replace(' ' * tab_size, '&nbsp;' * tab_size)

It really shouldn’t be trying to correct spaces, it should be trying to give the closes representation to what is in the editor. Converting a tab to spaces makes sense.[/quote]

I changed it to your suggestion:** tidied_text = tidied_text.replace(’ ', ’ ')** - we need nbsp to maintain mono-space-like behaviour.

Is this still an issue? I copied your code but it worked fine for me(?).

PS This is the problem with using spaces to fake alignment :smiley:

0 Likes

#52

Would it help if you used

 tags?

(I’m just butting into this topic since I understand ~1% of it.)

0 Likes

#53

[quote=“agibsonsw”]

[quote=“facelessuser”]Also, this is really a show stopper for me:

tidied_text = tidied_text.replace(' ' * tab_size, ' ' * tab_size)

It really shouldn’t be trying to correct spaces, it should be trying to give the closes representation to what is in the editor. Converting a tab to spaces makes sense.[/quote]

I changed it to your suggestion:** tidied_text = tidied_text.replace(’ ', ’ ')** - we need nbsp to maintain mono-space-like behaviour.

Is this still an issue? I copied your code but it worked fine for me(?).

PS This is the problem with using spaces to fake alignment :smiley:[/quote]

Did you push it to the repository? The actual repository shows that you did not change it. I must not have the latest. Can you double check?

0 Likes

#54

[quote=“C0D312”]Would it help if you used

 tags?

(I’m just butting into this topic since I understand ~1% of it.)[/quote]

PRE may be an alternative, but it presents other issues in terms of formatting within them (I think). I’m hoping, having replaced all spaces with  , that this issue is resolved. But this is more to do with the way HTML behaves, rather than a faulty approach within my code :wink:

I should look again at just leaving the spaces, but they tend to *disappear *quite often, he, he. And creating empty elements of a certain size is a minefield - which probably won’t work anyway.

I was going to suggest that you can’t go wrong with tabs… but tabs are not always four spaces, Doh! Another issue for later…

@facelessuser: I haven’t updated my repo recently. My recent code is below, but I want to fix the disappearing lines issue before I update. That is, if there’s a completely blank line (when not printing line numbers) I want to insert a   to prevent it collapsing.

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

class PrintHtmlCommand(sublime_plugin.TextCommand):
def run(self, edit, numbers): # numbers == True: output line numbers
self.colours = {}
path_packages = sublime.packages_path()
settings = sublime.load_settings(‘Preferences.sublime-settings’)
colour_scheme = settings.get(‘color_scheme’)
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_top = settings.get(‘line_padding_top’) or 0
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 = ‘’; gfground = ‘’
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()
elif key_tag.firstChild.data.strip() == ‘gutterForeground’:
gfground = key_tag.nextSibling.nextSibling.firstChild.data.strip()
except:
pass
if gfground == ‘’: gfground = fground
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()
	if curr_file is None:
		fname = 'temp'
	else:
		head, tail = path.split(curr_file)
		fname, ext = path.splitext(tail)
		fname = fname + ext.replace('.', '_')
		fname = head + path.sep + fname
	
	curr_sel = curr_view.sel()[0]
	if curr_sel.empty() or abs(curr_sel.end() - curr_sel.begin()) < 4:
		the_html = open(fname + '_parsed.html', 'w')
		size = curr_view.size()
		pt = 0; end = 1
		partial = False
	else:
		the_html = open(fname + '_part.html', 'w')
		size = curr_sel.end()
		pt = curr_sel.begin()
		end = pt + 1
		curr_row = curr_view.rowcol(pt)[0] + 1
		partial = True
	the_html.write('<!DOCTYPE html>\n')
	the_html.write('<html>\n<head>\n<title>' + fname + '</title>\n')
	the_html.write('<style type=\"text/css\">\n')
	the_html.write('\tspan { display: inline; border: 0; margin: 0; padding: 0; }\n')
	if not numbers:
		the_html.write('\tol { list-style-type: none; }\n')
	the_html.write('\tli { color: ' + gfground  + '; margin-top: ' + 
		`padd_top` + 'pt; margin-bottom: ' + `padd_bottom` + 'pt; }\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')
	if numbers and partial:
		the_html.write('<ol>\n<li value="%d">' % curr_row)		# use code's line numbering
	else:
		the_html.write('<ol>\n<li>')
	default_scope = curr_view.scope_name(end).split(' ')[0]
	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 the_key == default_scope:
				self.colours[the_key] = fground
				the_colour = fground
			else:
				best_match = 0
				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]
				if curr_view.score_selector(pt, default_scope) > best_match:
					the_colour = fground
				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(' ', '&nbsp;')
		new_li = '</span></li>\n<li><span style=\"color:' + the_colour + '\">'
		if len(tidied_text) == 2: print ',' + tidied_text + ','
		tidied_text = tidied_text.replace('\n', new_li)
		the_html.write('<span style=\"color:' + the_colour + '\">')
		the_html.write(tidied_text + '</span>')
		pt = end
		end = pt + 1
	the_html.write('</li>\n</ol>')
	the_html.write('\n</body>\n</html>')
	the_html.close()[/code]
0 Likes