""" Sublime code highlighting for tooltips. Licensed under MIT Copyright (c) 2015 - 2020 Isaac Muse <isaacmuse@gmail.com> """ import re from pygments import highlight from pygments.lexers import get_lexer_by_name, guess_lexer from pygments.formatters import find_formatter_class HtmlFormatter = find_formatter_class('html') pygments = True html_re = re.compile( r'''(?x) (?P<start><span [^<>]+>)|(?P<content>[^<>]+)|(?P<end></span>) ''' ) multi_space = re.compile(r'(?<= ) {2,}') def replace_nbsp(m): """Replace spaces with `nbsp`.""" return ' ' * len(m.group(0)) class SublimeWrapBlockFormatter(HtmlFormatter): """Format the code blocks.""" def wrap(self, source, outfile): """Overload wrap.""" return self._wrap_code(source) def _wrap_code(self, source): """ Wrap the Pygments code. Sublime popups don't really support 'pre', but since it doesn't hurt anything, we leave it in for the possibility of future support. We get around the lack of proper 'pre' support by converting any spaces after the initial space to `nbsp`. We go ahead and convert tabs to 4 spaces as well. We also manually inject line breaks. """ yield 0, '<div class="%s"><pre>' % self.cssclass for i, t in source: text = '' matched = False for m in html_re.finditer(t): matched = True if m.group(1): text += m.group(1) elif m.group(3): text += m.group(3) else: text += multi_space.sub( replace_nbsp, m.group(2).replace('\t', ' ' * 4) ).replace(''', '\'').replace('"', '"') if not matched: text = multi_space.sub( replace_nbsp, t.replace('\t', ' ' * 4) ).replace(''', '\'').replace('"', '"') if i == 1: # it's a line of formatted code text += '<br>' yield i, text yield 0, '</pre></div>' class SublimeBlockFormatter(HtmlFormatter): """Format the code blocks with wrapping.""" def wrap(self, source, outfile): """Overload wrap.""" return self._wrap_code(source) def _wrap_code(self, source): """ Wrap the Pygments code. Sublime popups don't really support 'pre', but since it doesn't hurt anything, we leave it in for the possibility of future support. We get around the lack of proper 'pre' support by converting any spaces after the initial space to `nbsp`. We go ahead and convert tabs to 4 spaces as well. We also manually inject line breaks. """ yield 0, '<div class="%s"><pre>' % self.cssclass for i, t in source: text = '' matched = False for m in html_re.finditer(t): matched = True if m.group(1): text += m.group(1) elif m.group(3): text += m.group(3) else: text += m.group(2).replace( '\t', ' ' * 4 ).replace(' ', ' ').replace(''', '\'').replace('"', '"') if not matched: text = t.replace('\t', ' ' * 4).replace( ' ', ' ' ).replace(''', '\'').replace('"', '"') if i == 1: # it's a line of formatted code text += '<br>' yield i, text yield 0, '</pre></div>' class SublimeInlineHtmlFormatter(HtmlFormatter): """Format the code blocks.""" def wrap(self, source, outfile): """Overload wrap.""" return self._wrap_code(source) def _wrap_code(self, source): """ Wrap the Pygments code. Sublime popups don't really support 'code', but since it doesn't hurt anything, we leave it in for the possibility of future support. We get around the lack of proper 'code' support by converting any spaces after the initial space to `nbsp`. We go ahead and convert tabs to 4 spaces as well. """ yield 0, '<code class="%s">' % self.cssclass for i, t in source: text = '' matched = False for m in html_re.finditer(t): matched = True if m.group(1): text += m.group(1) elif m.group(3): text += m.group(3) else: text += multi_space.sub( replace_nbsp, m.group(2).replace('\t', ' ' * 4) ).replace(''', '\'').replace('"', '"') if not matched: text = multi_space.sub( replace_nbsp, t.replace('\t', ' ' * 4) ).replace(''', '\'').replace('"', '"') yield i, text yield 0, '</code>' def syntax_hl(src, lang=None, guess_lang=False, inline=False, code_wrap=False): """Highlight.""" css_class = 'highlight' src = src.strip('\n') try: lexer = get_lexer_by_name(lang) except ValueError: try: if guess_lang: lexer = guess_lexer(src) else: lexer = get_lexer_by_name('text') except ValueError: lexer = get_lexer_by_name('text') if inline: formatter = SublimeInlineHtmlFormatter( cssclass=css_class ) elif code_wrap: formatter = SublimeWrapBlockFormatter( cssclass=css_class ) else: formatter = SublimeBlockFormatter( cssclass=css_class ) return highlight(src, lexer, formatter)