Sublime Forum

Copy text with highlighted colors

#81

[quote=“agibsonsw”]I said I would publish this Wednesday - I meant Thursday, the weekend has thrown out my calendar?!

Having split the text into numbered items (li’s) I realise I should also have split the view content into lines as well. This has several advantages:

I wouldn’t need a complex process to find, and remove, the ‘\n’ newline characters;
A simple if, or else, would determine that there was a completely blank line;
The likelihood of an empty span would be much reduced;
A nice generator function could process each span;
The file could be written-to one line at a time.

Also, perhaps, the spans’ text and colours could be kept in a temporary list of tuples, and then HTML-encoded, etc., in one shot (per line), before being written to the file.

I know it works *great *as it is, but I think it might be worth the effort to redraft. I shall consider, and perhaps tackle, this tomorrow. Andy.[/quote]

I felt the same way with using ol/li. So I threw them out of mine. I process each line of the view separately. I now have complete control of explicitly setting line numbers and even controlling style (no dot after gutter number, etc.)

I also wrapped the whole thing in a pre to better preserve format better.

I put each line in a table row to keep prevent ugly wrap when you copy and paste the HTML to an email.

I also keep the background color on copy and paste.

I want to add an option to support highlighting showing selection highlights…I spent too much time on this last night…I need to put this down… :open_mouth:

I blame you @agibsonsw, why did you have to post this plugin :smile:.

0 Likes

#82

Tables! :smiling_imp: :unamused: I’ve cancelled your (belated) Easter egg. Although… the copy table add-in for FireFox is cool - the one for Chrome is not so good.

 containing a table?! So much for CSS and the semantic web!

[code]if ‘gutterForeground’ in colour_settings:
self.gfground = colour_settings"gutterForeground"].strip()

if self.gfground == ‘’:
self.gfground = self.fground[/code]
Using the syntax from a previous post, presumably:

self.gfground =  colour_settings"gutterForeground"].strip() if 'gutterForeground' in colour_settings else self.fground

would also work, making sure that .fground is already assigned a value.

I initially looked at extract_scope(), rather than incrementing through each character, but it kept swallowing the whole document region. It occurred to me that removing ‘source.lang’ from each scope would prevent this, but I don’t think there’s much to be gained from this revision. Added: besides, it would extend past EOL anyway.

Rather than just switching off the search as soon as a new scope is reached, I am considering continuing past spaces and tabs. This way, there won’t be nearly as many messy, empty-ish, spans; it’ll be neater.

I did mention highlighting the selection previously, but didn’t know that you would run with the idea :laughing:.

You obviously still have too much time on your hands :wink: . At some stage you might make a pull request. I shall continue to explore. I might detour to work with a non-table/pre version. Andy.

0 Likes

#83

[quote=“agibsonsw”]
You obviously still have too much time on your hands :wink: . At some stage you might make a pull request. I shall continue to explore. I might detour to work with a non-table/pre version. Andy.[/quote]

Wife had a writers group last night. I was stuck in the back room. Sometimes when something gets in my head I can’t stop until I shake it out.

This is the web output…with selections, gutter background support, and all. I will make a pull request tonight. I am no web programmer, so I am not always sure of all the “right” ways to do things. But it seems to look good in the browsers I have tried.

0 Likes

#84

@facelessuser

I know what you mean: the ‘dotted name’ issue I’ve mentioned previously is always at the back of my mind - it won’t go away!

Tables are (kinda) deprecated for “non-table” layouts. But, because we’re not really building a web-page, but trying to reproduce the mono-spaced behaviour of code, we can get away with it. Using non-breaking spaces all over the shop is also a no-no. But, hey :smiley:

‘pre’ is intended for text that we might type directly into HTML - not for text that we construct. The behaviour of other elements (spans, etc.) within pre-tags is not clearly defined or documented. But, again, hey :smiley:

The line-numbering, and it’s dot, could have been controlled with css. The advantage of this would have been that the user could easily modify the css. This was also why I at an earlier point considered using (a version of) the scope name(s) as class-ids for the spans. However, the users’ ability to attach a printable-theme is a nice alternative.

(Finally) I assume there might be a plug-in or function somewhere to entity-escape/encode HTML. However, as we only need to encode a few characters (> < &) I would be reluctant to attach (yet another) plug.

Andy.

0 Likes

#85

Yeah, that is kind of what I figured. We aren’t really building a web site, just a page of code that can be shared or printed…with color :wink:.

The way we parse the code, I don’t think it would have any real benefit to escape the whole view. The text wouldn’t line up with the view points then. About the best you could do is clean it up like with what you were playing around with, I believe it was zip. Other than that I think it is fine.

I have been toying with the idea of changing the plugin to allow you to feed the color scheme in as a parameter opposed to a setting. Seems it would be useful, like if you wanted to print in black and white, you could use a black and white theme. Print in color, color theme. Sharing in email, you could use a use current theme that may have a black background. That way the user can configure as many print option commands as they want.

Maybe better would be to allow you to configure different configurations (color with lines, black and white no lines, etc.) in the settings, and you could launch a command that would drop down the quick panel and let you select from all of your configurations that are found in the settings file.

Just thinking.

0 Likes

#86

@facelessuser

It could be possible :smiley: to ignore the theme altogether and create a minimal keyword/comment blue/green, etc., (a la VBA) dictionary.

I’m still having problems with my (repo) version, in that it still collapses (most) blank lines. For example, running it against PrintHtml:

[code]style=“color:#E8B2F2”>packages_path()

  • [/code] It's creating an empty span, or failing to insert
    within it, depending on ones' perspective. I've modified the code as follows, to skip spaces, etc.. Although this has dramatically reduced the number of empty spans, I still have the issue described.

    while self.end <= self.size and (self.view.scope_name(self.end) == scope_name or (self.view.substr(self.end) in '\r', '\n', '\t', ' ', ''])): self.end += 1
    There’s either a problem with the regex or there is some weird, **phantom **character sneaking through. *

    If I continue with my plan to read the view line-by-line (without newlines) then I think this problem might disappear, as I won’t really need the regex and it would/should be easier to check for an empty line.

    [At “some distant point in the future” :smile: I might even look at creating a Word document. I would minimize Word margins, tabs, etc., before building the doc. It wouldn’t be too tricky :wink: ]*

    0 Likes

    #87

    Actually, I need to modify the regex anyway (if I’m going to skip past \n).

    0 Likes

    #88

    [quote=“agibsonsw”]@facelessuser

    It could be possible :smiley: to ignore the theme altogether and create a minimal keyword/comment blue/green, etc., (a la VBA) dictionary.

    I’m still having problems with my (repo) version, in that it still collapses (most) blank lines. For example, running it against PrintHtml:

    [code]style=“color:#E8B2F2”>packages_path()

  • [/code] It's creating an empty span, or failing to insert
    within it, depending on ones' perspective. I've modified the code as follows, to skip spaces, etc.. Although this has dramatically reduced the number of empty spans, I still have the issue described.

    while self.end <= self.size and (self.view.scope_name(self.end) == scope_name or (self.view.substr(self.end) in '\r', '\n', '\t', ' ', ''])): self.end += 1
    There’s either a problem with the regex or there is some weird, **phantom **character sneaking through. *

    If I continue with my plan to read the view line-by-line (without newlines) then I think this problem might disappear, as I won’t really need the regex and it would/should be easier to check for an empty line.

    [At “some distant point in the future” :smile: I might even look at creating a Word document. I would minimize Word margins, tabs, etc., before building the doc. It wouldn’t be too tricky :wink: ]*

    Yeah, the older non-per-line version is just a hassle to do anything with. The more I tried to extend it, the more I realized I had to abandon it and split the view per lines.

    I have issued a pull request with my per line tinkering and such. The regex was a band-aide, the rework was (I think) a better solution. It is much easier to work with.[/quote]

    0 Likes

    #89

    Yes, I’m working on a per-line basis - feels much more comfortable without having to worry about \n or \r.

    Presumably I can test line.empty() and insert ‘


  • ’ - we’ll see :smile:

    **

  • 0 Likes

    #90

    [quote=“agibsonsw”]Yes, I’m working on a per-line basis - feels much more comfortable without having to worry about \n or \r.

    Presumably I can test line.empty() and insert ‘


  • ’ - we’ll see :smile:

    **

    Cool. Yeah, the cool thing about the webpage is you can access to general web features too.

    If you end up not needing/wanting my pull let me know and I will cancel it. No big deal either way. If you cook up something better, it would be better to run with what works best.[/quote]

  • 0 Likes

    #91

    @facelessuser

    I’ll pull a bit later - bit pre-occupied at the minute :smile:

    Mine’s shaping up :sunglasses:. I think all my empty spans have gone and the blank lines are preserved. I’ve just noticed that all the li’s (no line numbers) are indented. Was this there before? Only the filename at the top is flush-left. Looks kinda cool though, but I might amend the CSS to remove this gap.

    I copied the following line from your code but had to change ‘self.end’ to ‘self.pt’. I know our code is different at the moment, but thought it might be worth checking on yours :wink: - line 145 from your previous version.

    for line in self.view.split_by_newlines(sublime.Region(self.end, self.size)):

    At some point tomorrow I’ll “show my working” and we can take it from there.

    Andy.

    0 Likes

    #92

    Actually, you could have a shufty below. It’s looking good but I haven’t reviewed it in detail yet.

    [code]import sublime
    import sublime_plugin
    from os import path
    import tempfile
    import desktop
    import re
    import sys

    PACKAGE_SETTINGS = “PrintHtml.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

    class PrintHtmlCommand(sublime_plugin.TextCommand):
    def setup(self, numbers):
    path_packages = sublime.packages_path()

        # Get get general document preferences from sublime preferences
        settings = sublime.load_settings('Preferences.sublime-settings')
        self.font_size = settings.get('font_size') or 10
        self.font_face = settings.get('font_face') or 'Consolas'
        self.tab_size = settings.get('tab_size') or 4
        self.padd_top = settings.get('line_padding_top') or 0
        self.padd_bottom = settings.get('line_padding_bottom') or 0
        self.bground = ''
        self.fground = ''
        self.gfground = ''
        self.numbers = numbers
    
        # Get color scheme
        alt_scheme = sublime.load_settings(PACKAGE_SETTINGS).get("alternate_scheme", False)
        scheme_file = settings.get('color_scheme') if alt_scheme == False else alt_scheme
        colour_scheme = path.normpath(scheme_file)
        plist_file = readPlist(path_packages + colour_scheme.replace('Packages', ''))
        colour_settings = plist_file"settings"][0]"settings"]
    
        # Get general theme colors from color scheme file
        if "background" in colour_settings:
            self.bground = colour_settings"background"].strip()
        if 'foreground' in colour_settings:
            self.fground = colour_settings"foreground"].strip()
        if 'gutterForeground' in colour_settings:
            self.gfground = colour_settings"gutterForeground"].strip()
    
        if self.gfground == '':
            self.gfground = self.fground
    
        # Determine start and end points and whether to parse whole file or selection
        curr_sel = self.view.sel()[0]
        if curr_sel.empty() or abs(curr_sel.end() - curr_sel.begin()) < 4:
            self.size = self.view.size()
            self.pt = 0
            self.end = 1
            self.partial = False
        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.partial = True
    
        # Create scope colors mapping from color scheme file
        self.colours = {self.view.scope_name(self.end).split(' ')[0]: self.fground}
        for item in plist_file"settings"]:
            scope = None
            colour = None
            if 'scope' in item:
                scope = item'scope']
            if 'settings' in item and 'foreground' in item'settings']:
                colour = item'settings']'foreground']
    
            if scope != None and colour != None:
                self.colours[scope] = colour
    
    def guess_colour(self, the_key):
        the_colour = None
        if the_key in self.colours:
            the_colour = self.colours[the_key]
        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]
            self.colours[the_key] = the_colour
        return the_colour
    
    def write_header(self, the_html):
        the_html.write('<!DOCTYPE html>\n')
        the_html.write('<html>\n<head>\n<title>' + path.basename(the_html.name) + '</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 self.numbers:
            the_html.write('\tol { list-style-type: none; }\n')
        the_html.write('\tli { color: ' + self.gfground + '; margin-top: ' +
            str(self.padd_top) + 'pt; margin-bottom: ' + str(self.padd_bottom) + 'pt; }\n')
        the_html.write('\tbody { ')
        if self.fground != '':
            the_html.write('color: ' + self.fground + ';')
        if self.bground != '':
            the_html.write(' background-color: ' + self.bground + ';')
        the_html.write(' font: ' + str(self.font_size) + 'pt \"' + self.font_face + '\", Consolas, Monospace;')
        the_html.write('\n}\n')
        the_html.write('</style>\n</head>\n')
    
    def convert_view_to_html(self, the_html):
        for line in self.view.split_by_newlines(sublime.Region(self.pt, self.size)):
            self.pt = line.begin(); self.end = self.pt + 1
            if line.empty():
                the_html.write('<br/></li>\n<li>')
                continue
            self.line_end = line.end()
            while self.end <= self.line_end:
                scope_name = self.view.scope_name(self.pt)
                while (self.end <= self.line_end and (self.view.scope_name(self.end) == scope_name 
                        or (self.view.substr(self.end) in '\t', ' ', '']))):
                    self.end += 1
                region = sublime.Region(self.pt, self.end)
    
                the_colour = self.guess_colour(scope_name.strip())
                tidied_text = self.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;' * self.tab_size)
                tidied_text = tidied_text.replace(' ', '&nbsp;')
    
                the_html.write('<span style=\"color:' + the_colour + '\">')
                the_html.write(tidied_text + '</span>')
                self.pt = self.end
                self.end = self.pt + 1
            the_html.write('</li>\n<li>')
    
    def write_body(self, the_html):
        the_html.write('<body>\n')
    
        # Write file name
        fname = self.view.file_name()
        if fname == None or not path.exists(fname):
            fname = "Untitled"
        the_html.write('<span style=\"color:' + self.fground + '\">' + fname + '</span>\n')
    
        if self.numbers and self.partial:
            the_html.write('<ol>\n<li value="%d">' % self.curr_row)  # use code's line numbering
        else:
            the_html.write('<ol>\n<li>')
    
        # Convert view to HTML
        self.convert_view_to_html(the_html)
    
        the_html.write('</li>\n</ol>')
    
        # Write empty line to allow copying of last line and line number without issue
        the_html.write('\n<br/>\n</body>\n</html>')
    
    def run(self, edit, numbers):
        self.setup(numbers)
    
        with tempfile.NamedTemporaryFile(delete=False, suffix='.html') as the_html:
            self.write_header(the_html)
            self.write_body(the_html)
    
        # Open in web browser
        desktop.open(the_html.name)
    

    [/code]

    Added: Actually, it’s not that dissimilar to your previous version - apart from the table and my skipping past spaces and tabs. I haven’t yet written to the file by line though.

    0 Likes

    #93

    [quote=“agibsonsw”]@facelessuser

    I’ll pull a bit later - bit pre-occupied at the minute :smile:

    Mine’s shaping up :sunglasses:. I think all my empty spans have gone and the blank lines are preserved. I’ve just noticed that all the li’s (no line numbers) are indented. Was this there before? Only the filename at the top is flush-left. Looks kinda cool though, but I might amend the CSS to remove this gap.

    I copied the following line from your code but had to change ‘self.end’ to ‘self.pt’. I know our code is different at the moment, but thought it might be worth checking on yours :wink: - line 145 from your previous version.

    for line in self.view.split_by_newlines(sublime.Region(self.end, self.size)):

    At some point tomorrow I’ll “show my working” and we can take it from there.

    Andy.[/quote]

    Tough to say, I am not sure what revision you are working on. I am not even using li tags in the latest. In the latest, I just use a span like everything else and pad it to make sure it aligns with the largest line on the page. When I would paste in outlook, it would sometimes word wrap and look ugly. Now when I paste it in outlook, if it word wraps, since it is part of a table, it word wraps after the line number opposed to under it.

    Not sure what you mean by shufty. It appears you are looking at an old revision.

    0 Likes

    #94
    It appears you are looking at an old revision

    Yes, I was at work today :smile: so I’m a bit behind and I’m not using a table. Below is the only code I’ve changed from a earlier version (yesterday).

    [code] def convert_view_to_html(self, the_html):
    for line in self.view.split_by_newlines(sublime.Region(self.pt, self.size)):
    self.pt = line.begin(); self.end = self.pt + 1
    if line.empty():
    the_html.write(’
    \n

  • ’)
    continue
    self.line_end = line.end()
    while self.end <= self.line_end:
    scope_name = self.view.scope_name(self.pt)
    while (self.end <= self.line_end and (self.view.scope_name(self.end) == scope_name
    or (self.view.substr(self.end) in ‘\t’, ’ ', ‘’]))):
    self.end += 1
    region = sublime.Region(self.pt, self.end)
                the_colour = self.guess_colour(scope_name.strip())
                tidied_text = self.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;' * self.tab_size)
                tidied_text = tidied_text.replace(' ', '&nbsp;')
    
                the_html.write('<span style=\"color:' + the_colour + '\">')
                the_html.write(tidied_text + '</span>')
                self.pt = self.end
                self.end = self.pt + 1
            the_html.write('</li>\n<li>')[/code]
  • 0 Likes

    #95

    Wow, you’re up-to 300 lines! Not much point in looking at my previous code sample then :laughing:

    [quote]shufty
    The English form of shufti (arabic for look / take a look)

    Originally RAF but later Army also, shufty or shufti became taking a look for possible dangers. [/quote]

    0 Likes

    #96

    I am actively using it at work, so some of these issues have cropped up for me, so I fix them inline sometimes as I am going. All the major work happened last night…and I regret how much time I spent.

    The lines collapsing is why I reverted to using the pre. I no longer had to capture the \n. For some reason I think I still strip them and I just right the single one at the end. I think I was sometimes getting phantom ‘\n’. I need to look at that, I might not need to do that anymore.

    0 Likes

    #97

    [quote=“agibsonsw”]Wow, you’re up-to 300 lines! Not much point in looking at my previous code sample then :laughing:

    [quote]shufty
    The English form of shufti (arabic for look / take a look)

    Originally RAF but later Army also, shufty or shufti became taking a look for possible dangers. [/quote]

    [/quote]

    Yeah. Like I said…too much time. I am probably taking a break the next couple of days.

    0 Likes

    #98

    I noticed you were still replacing ‘\n’ with ‘’ when split_by_newlines should remove this need - but it can’t do any harm.

    I’ll read through your code in more detail tomorrow. Laterz, Andy.

    0 Likes

    #99

    @facelessuser

    On reflection, I won’t be releasing this myself as a plug-in. I’m not a programmer by trade, just an enthusiast, and this is not a feature that I will use often myself. Therefore, I’m unlikely to retain enough interest in it to maintain, and support, a plug-in.

    You have (now!) invested more time in it than I and, as you stated, it’s something that you are likely to continue to use. So I’m happy for you to release it yourself, and shall continue to take an interest in your code :sunglasses:, and support your progress.

    If it doesn’t interfere, I’ll not pull your code to mine, as there are still a few things I want to do with my (little…) version.

    Regards, Andy.

    Added: How do I cancel your pull request, or do you need to withdraw it? As I’m unable to update/commit my own update at the moment.

    0 Likes

    #100

    [quote=“agibsonsw”]@facelessuser

    On reflection, I won’t be releasing this myself as a plug-in. I’m not a programmer by trade, just an enthusiast, and this is not a feature that I will use often myself. Therefore, I’m unlikely to retain enough interest in it to maintain, and support, a plug-in.

    You have (now!) invested more time in it than I and, as you stated, it’s something that you are likely to continue to use. So I’m happy for you to release it yourself, and shall continue to take an interest in your code :sunglasses:, and support your progress.

    If it doesn’t interfere, I’ll not pull your code to mine, as there are still a few things I want to do with my (little…) version.

    Regards, Andy.

    Added: How do I cancel your pull request, or do you need to withdraw it? As I’m unable to update/commit my own update at the moment.[/quote]

    Not a problem. I have to say, for only an enthusiast, you seem to have the brain of a programmer :smile:. This was an excellent idea, and you broke it down well.
    Of course, you will have the credits for this when/if it gets released.

    Now there seems to be a couple of plugins that do the same thing…well not exactly the same (yours is the only one that seems to duplicate sublimes colors). I will have to evaluate if people want another plugin for converting to HTML. But nevertheless, I am going to run with mine; I find it fun tinkering with this :smile:.

    I will let you know if it gets released.

    A couple of side notes that I plan to be working on.
    -A setting to enable a prettier HTML wrapping. I don’t plan on adding indentation, but just forcing the wrap after the line numbers at a certain user specified size. (probably use JavasScript)
    -Maybe a menu for printing different configurations.
    -Add a simple gray scale print color scheme.

    0 Likes