-
Notifications
You must be signed in to change notification settings - Fork 0
/
pygments.lua
201 lines (168 loc) · 6.64 KB
/
pygments.lua
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
label = 'Pygments'
about = [[
Format source code with pygments
]]
function ensurePreamble(model)
local preamble_begin_marker = '%%% BEGIN PYGMENTS PREAMBLE %%%'
local use_packages = '\\usepackage{color}\n\\usepackage{fancyvrb}'
local get_preamble_py_code = 'from pygments.formatters import LatexFormatter; print(LatexFormatter().get_style_defs())'
local props = model.doc:properties()
local preamble = props['preamble']
if preamble:find(preamble_begin_marker, 1, true) then return true end
if not prefs.pygments.python then model:warning('Missing setting prefs.pygments.python') return false end
local p, open_err = _G.io.popen(prefs.pygments.python .. ' -c "' .. get_preamble_py_code .. '"', 'r')
if not p then model:warning('Failed to execute pygment python: ' .. model_err) return false end
local defs, read_err = assert(p:read('*a'))
if not defs then model:warning('Failed to read from python process: ' .. read_err) return false end
local close, close_err = p:close()
if not close then model:warning('Failed to close python process: ' .. close_err) return false end
if preamble:len() > 0 then
preamble = preamble .. '\n\n'
end
preamble = preamble .. preamble_begin_marker .. '\n' .. use_packages .. '\n' .. defs .. '%%% END PYGMENTS PREAMBLE %%%\n\n'
model.doc:setProperties({preamble=preamble})
return true
end
function askLanguage(model)
local d = ipeui.Dialog(model.ui:win(), 'Pygments')
d:add('label1', 'label', {label='Language'}, 1, 1, 1, 1)
d:add('language', 'input', {}, 1, 2, 1, 1)
d:addButton('ok', '&Ok', 'accept')
d:addButton('cancel', '&Cancel', 'reject')
if not d:execute() then return end
return d:get('language')
end
function encodeInput(language, source_code)
local result = '%%% PYGMENTS LANGUAGE: ' .. language .. '\n%%% PYGMENTS SOURCE CODE BEGIN %%%\n'
for s in source_code:gmatch('[^\n]*\n?') do
result = result .. '% ' .. s
end
if not source_code:find('\n$') then
result = result .. '\n'
end
return result .. '%%% PYGMENTS SOURCE CODE END %%%\n\n'
end
function recoverInput(model, latex)
local language = latex:match('%%%%%% PYGMENTS LANGUAGE: (%w+)')
if not language then return end
local _, i = latex:find('%%%%%% PYGMENTS SOURCE CODE BEGIN %%%%%%\r?\n', 1)
if not i then return end
local j = latex:find('%%%%%% PYGMENTS SOURCE CODE END %%%%%%\r?\n', i + 1)
local commented_code = latex:sub(i + 1, j - 1)
local code = nil
for s in commented_code:gmatch('%% ([^\n]*)\n') do
if code then
code = code .. '\n' .. s
else
code = s
end
end
return language, code
end
function ensureTextSelection(model)
local p = model:page()
local prim = p:primarySelection()
if not prim then model.ui:explain('no selection') return end
local obj = p[prim]
if obj:type() ~= 'text' then model:warning('Primary selection is not a text object') return end
return obj
end
function doFormat(model, obj, source_code, language, action_label)
local result = encodeInput(language, source_code)
local tmp_in = config.latexdir .. 'ipe-pygments_tmp-in'
local tmp_out = config.latexdir .. 'ipe-pygments_tmp-out'
local file_in, file_in_err = _G.io.open(tmp_in, 'w')
if not file_in then model:warning('Failed to open temp file ' .. tmp_in .. ': ' .. file_in_err) return end
local write, write_err = file_in:write(source_code)
if not write then model:warning('Failed to write to temp file: ' .. write_err) return end
local close1, close1_err = file_in:close()
if not close1 then model:warning('Failed to close temp file: ' .. close1_err) return end
if not prefs.pygments.pygmentize then model:warning('Missing settings prefs.pygments.pygmentize') return end
_G.os.execute(prefs.pygments.pygmentize .. ' -l ' .. language .. ' -f latex -o ' .. tmp_out .. ' ' .. tmp_in)
local file_out, file_out_err = _G.io.open(tmp_out, 'r')
if not file_out then model:warning('Failed to open temp file ' .. tmp_out .. ': ' .. file_out_err) return end
local pygments_code, read_err = file_out:read('*a')
if not pygments_code then model:warning('Failed to read from temp file: ' .. read_err) return end
result = result .. pygments_code
local close2, close2_err = file_out:close()
if not close2 then model:warning('Failed to close temp file: ' .. close2_err) return end
local remove1, remove1_err = _G.os.remove(tmp_in)
if not remove1 then model:warning('Failed to delete temp file ' .. tmp_in .. ': ' .. remove1_err) return end
local remove2, remove2_err = _G.os.remove(tmp_out)
if not remove2 then model:warning('Failed to delete temp file ' .. tmp_out .. ': ' .. remove2_err) return end
local t = {
label=action_label,
pno=model.pno,
vno=model.vno,
minipage=obj:get('minipage'),
text=obj:text(),
obj=obj,
result=result,
}
t.redo = function (t)
t.obj:setText(t.result)
t.obj:set('minipage', true)
-- Calling runLatex here to show results segfaults ipe :(
end
t.undo = function (t)
t.obj:setText(t.text)
t.obj:set('minipage', t.minipage)
end
t.redo(t)
model:register(t)
end
function format(model)
local obj = ensureTextSelection(model)
if not obj then return end
local language = askLanguage(model)
if not language then return end
if not ensurePreamble(model) then return end
local source_code = obj:text()
doFormat(model, obj, source_code, language, 'Pygments format')
end
function revert(model)
local obj = ensureTextSelection(model)
if not obj then return end
local latex = obj:text()
local _language, code = recoverInput(model, latex)
if not code then model:warning('Nothing to revert') return end
local t = {
label='Pygments revert',
pno=model.pno,
vno=model.vno,
text=obj:text(),
obj=obj,
code=code,
}
t.redo = function (t)
t.obj:setText(t.code)
end
t.undo = function (t)
t.obj:setText(t.text)
end
t.redo(t)
model:register(t)
end
function edit(model)
if not ensurePreamble(model) then return end
local obj = ensureTextSelection(model)
if not obj then return end
local latex = obj:text()
local language, code = recoverInput(model, latex)
if not code then model:warning('No encoded source code found') return end
local d = ipeui.Dialog(model.ui:win(), 'Pygments')
d:add('label1', 'label', {label='Language'}, 1, 1, 1, 1)
d:add('language', 'input', {}, 1, 2, 1, 1)
d:set('language', language)
d:add('code', 'text', {}, 2, 1, 1, 2)
d:set('code', code)
d:addButton('ok', '&Ok', 'accept')
d:addButton('cancel', '&Cancel', 'reject')
if not d:execute() then return end
doFormat(model, obj, d:get('code'), d:get('language'), 'Pygments edit')
end
methods = {
{ label='Format', run=format },
{ label='Edit', run=edit },
{ label='Revert', run=revert },
}