-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpygments.plugin.coffee
More file actions
154 lines (129 loc) · 3.92 KB
/
pygments.plugin.coffee
File metadata and controls
154 lines (129 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# Export Plugin
module.exports = (BasePlugin) ->
# Requires
removeIndentation = require('remove-indentation')
safeps = require('safeps')
{TaskGroup} = require('taskgroup')
jsdom = require('jsdom')
# Pygmentize some source code
# next(err,result)
pygmentizeSource = (source, language, next, attempt) ->
# Prepare
attempt ?= 0
result = ''
errors = ''
command = ['-f', 'html', '-O', 'encoding=utf-8']
# Language
if language
command.unshift(language)
command.unshift('-l')
else
command.unshift('-g')
# Process
command.unshift('pygmentize')
# Fire process
safeps.spawn command, {stdin:source}, (err,stdout,stderr) ->
# Error?
return next(err, null) if err
# Prepare
result = stdout or ''
# Render failed
# This happens sometimes, it seems when guessing the language pygments is every sporadic
if result is '' and attempt < 3
return pygmentizeSource(source, language, next, attempt+1)
# All good, return
return next(null, result)
# Chain
@
# Highlight an element
# next(err)
highlightElement = (window, element, next) ->
# Prepare
topNode = element
bottomNode = element
source = false
language = false
# Is our code wrapped inside a child node?
bottomNode = element
while bottomNode.childNodes.length and String(bottomNode.childNodes[0].tagName).toLowerCase() in ['pre','code']
bottomNode = bottomNode.childNodes[0]
# Is our code wrapped in a parentNode?
topNode = element
while topNode.parentNode.tagName.toLowerCase() in ['pre','code']
topNode = topNode.parentNode
# Check if we are already highlighted
if /highlighted/.test(topNode.className)
next()
return @
# Grab the source and language
source = removeIndentation(bottomNode.innerHTML)
language = bottomNode.getAttribute('lang') or topNode.getAttribute('lang')
language = String(language).replace(/^\s+|\s+$/g,'') if language
unless language
if bottomNode.className.indexOf('no-highlight') isnt -1
language = false
else
matches = bottomNode.className.match(/lang(?:uage)?-(\w+)/)
if matches and matches.length is 2
language = matches[1]
else
if topNode.className.indexOf('no-highlight') isnt -1
language = false
else
matches = topNode.className.match(/lang(?:uage)?-(\w+)/)
if matches and matches.length is 2
language = matches[1]
# Pygmentize
pygmentizeSource source, language, (err,result) ->
return next(err) if err
if result
# Handle
resultElWrapper = window.document.createElement('div')
resultElWrapper.innerHTML = result
resultElInner = resultElWrapper.childNodes[0]
resultElInner.className += ' highlighted codehilite'
topNode.parentNode.replaceChild(resultElInner, topNode)
return next()
# Chain
@
# Define Plugin
class PygmentsPlugin extends BasePlugin
# Plugin name
name: 'pygments'
# Render the document
renderDocument: (opts,next) ->
# Prepare
{extension,file} = opts
# Handle
if file.type is 'document' and extension is 'html'
# Create DOM from the file content
jsdom.env(
html: "<html><body>#{opts.content}</body></html>"
features:
QuerySelector: true
done: (err,window) ->
# Check
return next(err) if err
# Find highlightable elements
elements = window.document.querySelectorAll(
'code pre, pre code, .highlight'
)
# Check
if elements.length is 0
return next()
# Tasks
tasks = new TaskGroup().setConfig(concurrency:0).once 'complete', (err) ->
return next(err) if err
# Apply the content
opts.content = window.document.body.innerHTML
# Completed
return next()
# Syntax highlight those elements
(key for value,key in elements).forEach (key) -> tasks.addTask (complete) ->
element = elements.item(key)
highlightElement(window, element, complete)
# Run
tasks.run()
)
else
return next()