1if not modules then modules = { } end modules ['util-sci'] = {
2 version = 1.001,
3 comment = "companion to m-scite.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9local gsub, sub, find = string.gsub, string.sub, string.find
10local concat = table.concat
11local formatters = string.formatters
12local lpegmatch = lpeg.match
13local setmetatableindex = table.setmetatableindex
14
15local scite = scite or { }
16utilities.scite = scite
17
18local report = logs.reporter("scite")
19
20do
21
22
23 lexerroot = file.dirname(resolvers.findfile("scite-context-lexer.lua"))
24
25 if lfs.isdir(lexerroot) then
26
27 package.extraluapath(lexerroot)
28 package.extraluapath(lexerroot.."/themes")
29 package.extraluapath(lexerroot.."/data")
30 report("using lexer root %a",lexerroot)
31 else
32 report("no valid lexer root")
33 end
34end
35
36local knownlexers = {
37 tex = "tex",
38 mkiv = "tex", mkvi = "tex",
39 mkxl = "tex", mklx = "tex",
40 mkxi = "tex", mkix = "tex",
41 mkii = "tex",
42 bib = "bibtex",
43 cld = "tex",
44 lua = "lua", lmt = "lua",
45 lfg = "lua", lus = "lua", luv = "lua",
46 mp = "mps",
47 mpiv = "mps",
48 mpxl = "mps",
49 mpii = "mps",
50 w = "web", ww = "web",
51 c = "cpp", h = "cpp",
52 cpp = "cpp", hpp = "cpp",
53 cxx = "cpp", hxx = "cpp",
54 xml = "xml", xsl = "xml", xsd = "xml", dtd = "xml",
55 lmx = "xml", ctx = "xml", rlx = "xml",
56 css = "xml",
57 rme = "txt",
58 txt = "txt",
59
60}
61
62lexers = nil
63
64local function loadscitelexer()
65 if not lexers then
66 lexers = require("scite-context-lexer")
67 lexers.styles = require("scite-context-theme")
68 if lexers then
69 (lexers.disablewordcheck or lexers.context.disablewordcheck)()
70 end
71 end
72 return lexer
73end
74
75local loadedlexers = setmetatableindex(function(t,k)
76 local l = knownlexers[k] or k
77 loadscitelexer()
78 local v = lexers.load(formatters["scite-context-lexer-%s"](l))
79 t[l] = v
80 t[k] = v
81 return v
82end)
83
84scite.loadedlexers = loadedlexers
85scite.knownlexers = knownlexers
86scite.loadscitelexer = loadscitelexer
87
88local f_fore_bold = formatters['.%s { display:inline; font-weight:bold; color:#%02X%02X%02X; }']
89local f_fore_none = formatters['.%s { display:inline; font-weight:normal; color:#%02X%02X%02X; }']
90local f_none_bold = formatters['.%s { display:inline; font-weight:bold; }']
91local f_none_none = formatters['.%s { display:inline; font-weight:normal; }']
92local f_div_class = formatters['<div class="%s">%s</div>']
93local f_linenumber = formatters['<div class="linenumber">%s</div>\n']
94local f_lineerror = formatters['<div class="linenumber lineerror">%s</div>\n']
95local f_div_number = formatters['.linenumber { display:inline-block; font-weight:normal; width:%sem; margin-right:2em; padding-right:.25em; text-align:right; color:#000000; }']
96local s_div_error = '.lineerror { font-weight:bold; color:#FFFFFF; background-color:#000000; }'
97
98local replacer_regular = lpeg.replacer {
99 ["<"] = "<",
100 [">"] = ">",
101 ["&"] = "&",
102}
103
104local linenumber = 0
105local lineerror = 0
106local linenumbers = { }
107
108local replacer_numbered = lpeg.replacer {
109 ["<"] = "<",
110 [">"] = ">",
111 ["&"] = "&",
112 [lpeg.patterns.newline] = function()
113 linenumber = linenumber + 1
114 linenumbers[linenumber] = (linenumber == lineerror and f_lineerror or f_linenumber)(linenumber)
115 return "\n"
116 end,
117}
118
119local css = nil
120
121local function exportcsslexing()
122 if not css then
123 loadscitelexer()
124 local function black(f)
125 return (#f == 0 and f[1] == 0) or ((f[1] == f[2]) and (f[2] == f[3]) and (f[3] == 0))
126 end
127 local result, r = { }, 0
128 for k, v in table.sortedhash(lexers.context.styles) do
129 local bold = v.bold
130 local fore = v.fore
131 r = r + 1
132 if fore and not black(fore) then
133 local cr, cg, cb = fore[1], fore[2], fore[3]
134 result[r] = (bold and f_fore_bold or f_fore_none)(k,cr,cg or cr,cb or cr)
135 else
136 result[r] = (bold and f_none_bold or f_none_none)(k)
137 end
138 end
139 css = concat(result,"\n")
140 end
141 return css
142end
143
144local function exportwhites()
145 return setmetatableindex(function(t,k)
146 local v = find(k,"white",1,true) and true or false
147 t[k] = v
148 return v
149 end)
150end
151
152local function exportstyled(lexer,text,numbered)
153 local result = lexers.lex(lexer,text,0)
154 local start = 1
155 local whites = exportwhites()
156 local buffer = { }
157 local b = 0
158 linenumber = 0
159 linenumbers = { }
160 lineerror = tonumber(numbered) or 0
161 local replacer = numbered and replacer_numbered or replacer_regular
162 local n = #result
163 for i=1,n,2 do
164 local ii = i + 1
165 local style = result[i]
166 local position = result[ii]
167 local txt = sub(text,start,position-1)
168 txt = lpegmatch(replacer,txt)
169 b = b + 1
170 if whites[style] then
171 buffer[b] = txt
172 else
173 buffer[b] = f_div_class(style,txt)
174 end
175 start = position
176 end
177 return concat(buffer), concat(linenumbers)
178end
179
180local function exportcsslinenumber()
181 return f_div_number(#tostring(linenumber)/2+1) .. "\n" .. s_div_error
182end
183
184local htmlfile = utilities.templates.replacer([[
185<?xml version="1.0"?>
186<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
187 <html xmlns="http://www.w3.org/1999/xhtml">
188 <title>%title%</title>
189 <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
190 <style type="text/css"><!--
191%lexingstyles%
192%numberstyles%
193 --></style>
194 <body>
195 <table style="padding:0; margin:0; background-color:#FFFFFF;">
196 <tr>
197 <td><pre>%linenumbers%</pre></td>
198 <td><pre>%lexedcontent%</pre></td>
199 </tr>
200 </table>
201 </body>
202</html>
203]])
204
205local htmlblob = utilities.templates.replacer([[
206<style type="text/css"><!--
207%lexingstyles%
208%numberstyles%
209--></style>
210<table style="padding:0; margin:0; background-color:#FFFFFF;">
211 <tr>
212 <td><pre>%linenumbers%</pre></td>
213 <td><pre>%lexedcontent%</pre></td>
214 </tr>
215</table>
216]])
217
218function scite.tohtml(data,lexname,numbered,title)
219 local source, lines = exportstyled(loadedlexers[lexname],data or "",numbered)
220 if source then
221 return (title == false and htmlblob or htmlfile) {
222 lexedcontent = source,
223 lexingstyles = exportcsslexing(),
224 numberstyles = exportcsslinenumber(),
225 title = title or "context source file",
226 linenumbers = lines,
227 }
228 end
229end
230
231local function maketargetname(name)
232 if name then
233 return file.removesuffix(name) .. "-" .. file.suffix(name) .. ".html"
234 else
235 return "util-sci.html"
236 end
237end
238
239function scite.filetohtml(filename,lexname,targetname,numbered,title)
240 io.savedata(targetname or "util-sci.html",scite.tohtml(io.loaddata(filename),lexname or file.suffix(filename),numbered,title or filename))
241end
242
243function scite.css()
244 return exportcsslexing() .. "\n" .. exportcsslinenumber()
245end
246
247function scite.html(data,lexname,numbered)
248 return exportstyled(loadedlexers[lexname],data or "",numbered)
249end
250
251local f_tree_entry = formatters['<a href="%s" class="dir-entry">%s</a>']
252
253local htmlfile = utilities.templates.replacer([[
254<?xml version="1.0"?>
255<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
256 <html xmlns="http://www.w3.org/1999/xhtml">
257 <title>%title%</title>
258 <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
259 <style type="text/css"><!--
260%styles%
261 --></style>
262 <body>
263 <pre>
264%dirlist%
265 </pre>
266 </body>
267</html>
268]])
269
270function scite.converttree(sourceroot,targetroot,numbered)
271 if lfs.isdir(sourceroot) then
272 statistics.starttiming()
273 local skipped = { }
274 local noffiles = 0
275 dir.makedirs(targetroot)
276 local function scan(sourceroot,targetroot,subpath)
277 local tree = { }
278 for name in lfs.dir(sourceroot) do
279 if name ~= "." and name ~= ".." then
280 local sourcename = file.join(sourceroot,name)
281 local targetname = file.join(targetroot,name)
282 local mode = lfs.attributes(sourcename,'mode')
283 local path = subpath and file.join(subpath,name) or name
284 if mode == 'file' then
285 local filetype = file.suffix(sourcename)
286 local basename = file.basename(name)
287 local targetname = maketargetname(targetname)
288 local fullname = file.join(path,name)
289 if knownlexers[filetype] then
290 report("converting file %a to %a",sourcename,targetname)
291 scite.filetohtml(sourcename,nil,targetname,numbered,fullname)
292 noffiles = noffiles + 1
293 tree[#tree+1] = f_tree_entry(file.basename(targetname),basename)
294 else
295 skipped[filetype] = true
296 report("no lexer for %a",sourcename)
297 end
298 else
299 dir.makedirs(targetname)
300 scan(sourcename,targetname,path)
301 tree[#tree+1] = f_tree_entry(file.join(name,"files.html"),name)
302 end
303 end
304 end
305 report("saving tree in %a",targetroot)
306 local htmldata = htmlfile {
307 dirlist = concat(tree,"\n"),
308 styles = "",
309 title = path or "context dir listing",
310 }
311 io.savedata(file.join(targetroot,"files.html"),htmldata)
312 end
313 scan(sourceroot,targetroot)
314 if next(skipped) then
315 report("skipped filetypes: %a",table.concat(table.sortedkeys(skipped)," "))
316 end
317 statistics.stoptiming()
318 report("conversion time for %s files: %s",noffiles,statistics.elapsedtime())
319 end
320end
321
322
323
324
325
326
327return scite
328 |