Skip to content

Commit c0dc50a

Browse files
committed
syntax-highlight
0 parents  commit c0dc50a

7 files changed

+958
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__pycache__

LICENSE

+674
Large diffs are not rendered by default.

README.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
Syntax highlight plugin
2+
====================
3+
4+
Intro
5+
-----
6+
7+
This plugins syntax highlights code segments between `\`\`\`` and `<code></code>` tags.
8+
9+
Requires:
10+
11+
* pygments
12+
13+
Installation
14+
------------
15+
16+
the installation is simple as:
17+
18+
```sh
19+
mkdir -p ~/.config/astroid/plugins/
20+
cd ~/.config/astroid/plugins/
21+
git clone https://github.com/astroidmail/syntax-highlight
22+
```
23+
...and restart astroid.
24+
25+
Licensing
26+
---------
27+
28+
See [LICENSE](./LICENSE) for licensing information.
29+

syntax-highlight.plugin

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[Plugin]
2+
Module=syntax_highlight_plugin
3+
Depends=
4+
Loader=python3
5+
Name=Syntax Highlight plugin
6+
Description=plugin
7+
Authors=Gaute Hope
8+
Copyright=GPLv3
9+
Website=
10+
Help=
11+
Hidden=false

syntax_highlight.py

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#! /usr/bin/env python
2+
import gi
3+
try:
4+
gi.require_version ('GMime', '3.0')
5+
except ValueError:
6+
gi.require_version ('GMime', '2.6')
7+
8+
from gi.repository import GMime
9+
10+
from pygments import highlight
11+
from pygments.lexers import guess_lexer
12+
from pygments.formatters import HtmlFormatter
13+
14+
class SyntaxHighlight:
15+
16+
def high (self, segment):
17+
lexer = guess_lexer (segment)
18+
19+
print ("filtering: guessed language:", str(lexer))
20+
21+
return highlight (segment, lexer, HtmlFormatter (noclasses = True))
22+
23+
def do_filter_part (self, text, html, mime_type, is_patch):
24+
"""
25+
Filter the part and output safe HTML.
26+
27+
Search for code tags (``` or <code>), or determine if part is a patch.
28+
Syntax highlight the relevant parts and use the html part for the rest.
29+
"""
30+
31+
## Try to figure out if part is a patch
32+
if is_patch:
33+
from pygments.lexers.diff import DiffLexer
34+
return highlight (text, DiffLexer (), HtmlFormatter (noclasses = True))
35+
36+
## Look for code segments between code-tags
37+
starttags = [ '```', '<code>' ]
38+
endtags = [ '```', '</code>' ]
39+
40+
if mime_type == 'text/html':
41+
for tag, antitag in zip(starttags, endtags):
42+
i = 0
43+
44+
def tags ():
45+
nonlocal i
46+
i = html.find (tag, i)
47+
if i != -1:
48+
i += len (tag)
49+
yield i
50+
51+
for j in tags ():
52+
e = html.find (antitag, i)
53+
54+
if e != -1:
55+
segment = self.high (html[i:e])
56+
html = html[:i-len(tag)] + segment + html[e + len(antitag):]
57+
58+
i += len(segment) - len(tag)
59+
60+
else:
61+
break
62+
63+
return html
64+
65+
elif mime_type == 'text/plain':
66+
# The GMime filter has created the HTML line-for-line. So if we find the
67+
# code tag on a line, it matches the same line in the HTML part.
68+
69+
text_lines = text.split ('\n')
70+
html_lines = html.split ('\n')
71+
72+
no = 0
73+
offset = 0 # offset between HTML and TEXT part after syntax highlighting a segment
74+
while no < len(text_lines):
75+
l = text_lines[no]
76+
for tag, antitag in zip (starttags, endtags):
77+
it = l.find (tag)
78+
if it > -1:
79+
ih = html_lines[no + offset].find (tag)
80+
81+
# find end
82+
for eno,el in enumerate (text_lines[no:]):
83+
iet = el.find (antitag)
84+
ieh = html_lines[no + eno + offset].find (antitag)
85+
86+
if (eno > 0 and iet > -1) or (eno == 0 and iet > it):
87+
# found end tag
88+
segment = text_lines[no:no + eno+1]
89+
segment[0] = segment[0][it + len(tag):]
90+
segment[-1] = segment[-1][:iet]
91+
92+
html_segment = self.high ('\n'.join (segment)).split ('\n')
93+
94+
html_segment[0] = html_lines[no + offset][ih + len(tag):] + html_segment[0]
95+
html_segment[-1] = html_segment[-1] + html_lines[no + eno + offset][ieh + len(antitag):]
96+
97+
html_lines = html_lines[:no + offset] + html_segment + html_lines[no + eno + offset +1:]
98+
99+
offset += len(html_segment) - len(segment)
100+
no += eno
101+
break
102+
break
103+
no += 1
104+
105+
return '\n'.join(html_lines)
106+
107+
else:
108+
return html
109+
110+

syntax_highlight_plugin.py

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#! /usr/bin/env python
2+
import gi
3+
gi.require_version ('Astroid', '0.1')
4+
gi.require_version ('Gtk', '3.0')
5+
6+
from gi.repository import GObject
7+
from gi.repository import Gtk
8+
from gi.repository import Astroid
9+
10+
from syntax_highlight import SyntaxHighlight
11+
12+
class SyntaxHighlightPlugin (GObject.Object, Astroid.ThreadViewActivatable, SyntaxHighlight):
13+
object = GObject.property (type = GObject.Object)
14+
thread_view = GObject.property (type = Gtk.Box)
15+
16+
def do_activate (self):
17+
print ('syntax: activated', __file__)
18+
19+
def do_deactivate (self):
20+
print ('syntax: deactivated')
21+
22+
def do_filter_part (self, text, html, mime_type, is_patch):
23+
return SyntaxHighlight.do_filter_part (self, text, html, mime_type, is_patch)
24+
25+
26+
print ('syntax: plugin loaded')
27+

test_syntax.py

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#! /usr/bin/env python
2+
3+
import unittest
4+
5+
from syntax_highlight import *
6+
7+
class TestSyntaxHighlight (unittest.TestCase):
8+
9+
def test_html_py (self):
10+
html = '''
11+
<html>
12+
<body>
13+
<p>Some text</p>
14+
<pre>
15+
<code>
16+
import sys
17+
18+
def something (foo):
19+
return foo
20+
21+
sys.exit (1)
22+
</code>
23+
</pre>
24+
<p> some more text </p>
25+
</body>
26+
</html>
27+
'''
28+
29+
s = SyntaxHighlight ()
30+
31+
print ( s.do_filter_part ('', html, 'text/html', False) )
32+
33+
def test_text_c (self):
34+
text = '''```
35+
int main (int argc, char ** argv) {
36+
int a = 0;
37+
int b = 2;
38+
int c;
39+
40+
c = a + b;
41+
return c;
42+
}
43+
44+
```'''
45+
46+
html = '''```<br>
47+
int main (int argc, char ** argv) {<br>
48+
&nbsp; int a = 0;<br>
49+
&nbsp; int b = 2;<br>
50+
&nbsp; int c;<br>
51+
<br>
52+
&nbsp; c = a + b;<br>
53+
&nbsp; return c;<br>
54+
}<br>
55+
<br>
56+
```<br>
57+
<br>'''
58+
59+
s = SyntaxHighlight ()
60+
61+
print ( s.do_filter_part (text, html, 'text/plain', False) )
62+
63+
def test_two_segments (self):
64+
65+
text = '''```
66+
<html>
67+
<head>
68+
</head>
69+
</html>
70+
```
71+
72+
```
73+
import sys
74+
75+
def foo (bar):
76+
return bar + 1
77+
```
78+
79+
'''
80+
81+
82+
html = '''```<br>
83+
&lt;html&gt;<br>
84+
&nbsp; &lt;head&gt;<br>
85+
&nbsp; &lt;/head&gt;<br>
86+
&lt;/html&gt;<br>
87+
```<br>
88+
<br>
89+
```<br>
90+
import sys<br>
91+
<br>
92+
def foo (bar):<br>
93+
&nbsp; return bar + 1<br>
94+
```<br>
95+
<br>
96+
<br>'''
97+
98+
s = SyntaxHighlight ()
99+
100+
print ( s.do_filter_part (text, html, 'text/plain', False) )
101+
102+
if __name__ == '__main__':
103+
unittest.main ()
104+
105+
106+

0 commit comments

Comments
 (0)