if not modules then modules = { } end modules ['mtx-cache'] = { version = 1.001, comment = "companion to mtxrun.lua", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } local concat, sort, insert = table.concat, table.sort, table.insert local gsub, format, gmatch, find, upper = string.gsub, string.format, string.gmatch, string.find, string.upper local utfchar, utfgsub = utf.char, utf.gsub local sortedkeys, sortedhash, serialize = table.sortedkeys, table.sortedhash, table.serialize local helpinfo = [[ mtx-interface ConTeXt Interface Related Goodies 0.13 generate context mkii interface files equals generate scite interface generate bbedit interface files generate jedit interface files generate textpad interface files generate vim interface files create text files for commands and environments report commands to the console generate check file report the mening of commands show the internal representation of commands replace named characters by utf preprocess mkvi files to tex files [force,suffix] use given suffix for output files force action even when in doubt a pattern for meaning lookups ]] local application = logs.application { name = "mtx-interface", banner = "ConTeXt Interface Related Goodies 0.13", helpinfo = helpinfo, } local report = application.report scripts = scripts or { } scripts.interface = scripts.interface or { } local flushers = { } local userinterfaces = { 'en','cs','de','it','nl','ro','fr','pe' } local messageinterfaces = { 'en','cs','de','it','nl','ro','fr','pe','no' } local function collect(filename,class,data) if data then local result, r = { }, 0 for name, list in sortedhash(data) do r = r + 1 ; result[r] = format("keywordclass.%s.%s=\\\n",class,name) for i=1,#list do if i%5 == 0 then r = r + 1 ; result[r] = "\\\n" end r = r + 1 ; result[r] = format("%s ",list[i]) end r = r + 1 ; result[r] = "\n\n" end io.savedata(file.addsuffix(filename,"properties"),concat(result)) io.savedata(file.addsuffix(filename,"lua"),serialize(data,true)) else os.remove(filename) end end function flushers.scite(collected) local data = { } -- for interface, whatever in next, collected do -- data[interface] = whatever.commands -- end local function add(target,origin,field) if origin then local list = origin[field] if list then for i=1,#list do target[list[i]] = true end end end end -- for interface, whatever in next, collected do local combined = { } add(combined,whatever,"commands") add(combined,whatever,"environments") if interface == "common" then add(combined,whatever,"textnames") add(combined,whatever,"mathnames") end data[interface] = sortedkeys(combined) end -- collect("scite-context-data-interfaces", "context", data) collect("scite-context-data-metapost", "metapost", dofile(resolvers.findfile("mult-mps.lua"))) collect("scite-context-data-metafun", "metafun", dofile(resolvers.findfile("mult-fun.lua"))) collect("scite-context-data-context", "context", dofile(resolvers.findfile("mult-low.lua"))) collect("scite-context-data-tex", "tex", dofile(resolvers.findfile("mult-prm.lua"))) end function flushers.jedit(collected) for interface, whatever in next, collected do local commands = whatever.commands local environments = whatever.environments local result, r = { }, 0 r = r + 1 ; result[r] = "" r = r + 1 ; result[r] = "\n" r = r + 1 ; result[r] = "" r = r + 1 ; result[r] = "\t" r = r + 1 ; result[r] = "\t\t" for i=1,#commands do r = r + 1 ; result[r] = format("\t\t\t%s",commands[i]) end r = r + 1 ; result[r] = "\t\t" r = r + 1 ; result[r] = "\t" r = r + 1 ; result[r] = "" io.savedata(format("context-jedit-%s.xml",interface), concat(result),"\n") end end function flushers.bbedit(collected) for interface, whatever in next, collected do local commands = whatever.commands local environments = whatever.environments local result, r = { }, 0 r = r + 1 ; result[r] = "" r = r + 1 ; result[r] = "BBLMKeywordList" r = r + 1 ; result[r] = "" for i=1,#commands do r = r + 1 ; result[r] = format("\t\\%s",commands[i]) end r = r + 1 ; result[r] = "" io.savedata(format("context-bbedit-%s.xml",interface), concat(result),"\n") end end -- The Vim export is maintained by Nicola Vitacolonna: local function vimcollect(filename,class,data) if data then local result, r = { }, 0 local endline = " contained\n" if find(class,"^meta") then endline = "\n" end r = r + 1 ; result[r] = "vim9script\n\n" r = r + 1 ; result[r] = "# Vim syntax file\n" r = r + 1 ; result[r] = "# Language: ConTeXt\n" r = r + 1 ; result[r] = format("# Automatically generated by mtx-interface (%s)\n\n", os.date()) local n = 5 -- number of keywords per row for name, list in sortedhash(data) do local i = 1 while i <= #list do r = r + 1 ; result[r] = format("syn keyword %s%s", class, (gsub(name,"^%l",upper))) -- upper is fragile local j = 0 -- while i+j <= #list and j < n do -- if list[i+j] == "transparent" then -- this is a Vim keyword -- r = r + 1 ; result[r] = format(" %s[]",list[i+j]) -- else -- r = r + 1 ; result[r] = format(" %s",list[i+j]) -- end -- j = j + 1 -- end while j < n do local lij = list[i + j] if not lij then break elseif lij == "transparent" then -- this is a Vim keyword r = r + 1 ; result[r] = format(" %s[]",lij) else r = r + 1 ; result[r] = format(" %s",lij) end j = j + 1 end r = r + 1 ; result[r] = endline i = i + n end end io.savedata(file.addsuffix(filename,"vim"),concat(result)) else os.remove(filename) end end function flushers.vim(collected) local data = { } -- for interface, whatever in next, collected do -- data[interface] = whatever.commands -- end local function add(target,origin,field) if origin then local list = origin[field] if list then for i=1,#list do target[list[i]] = true end end end end -- for interface, whatever in next, collected do local combined = { } add(combined,whatever,"commands") add(combined,whatever,"environments") if interface == "common" then add(combined,whatever,"textnames") add(combined,whatever,"mathnames") end data[interface] = sortedkeys(combined) end -- vimcollect("context-data-interfaces", "context", data) -- vimcollect("context-data-metapost", "metapost", dofile(resolvers.findfile("mult-mps.lua"))) vimcollect("context-data-metafun", "metafun", dofile(resolvers.findfile("mult-fun.lua"))) vimcollect("context-data-context", "context", dofile(resolvers.findfile("mult-low.lua"))) vimcollect("context-data-tex", "tex", dofile(resolvers.findfile("mult-prm.lua"))) end function flushers.raw(collected) for interface, whatever in next, collected do local commands = whatever.commands local environments = whatever.environments for i=1,#commands do report(commands[i]) end end end function flushers.data(collected) return collected -- table.save("cont-en-filenames.lua",collected.filenames) end local textpadcreator = "mtx-interface-textpad.lua" function flushers.text(collected) for interface, whatever in next, collected do local commands = whatever.commands local environments = whatever.environments local c, cname = { }, format("context-commands-%s.txt",interface) local e, ename = { }, format("context-environments-%s.txt",interface) report("saving '%s'",cname) for i=1,#commands do c[#c+1] = format("\\%s",commands[i]) end io.savedata(cname,concat(c,"\n")) report("saving '%s'",ename) for i=1,#environments do e[#e+1] = format("\\start%s",environments[i]) e[#e+1] = format("\\stop%s", environments[i]) end io.savedata(format("context-environments-%s.txt",interface),concat(e,"\n")) end end function flushers.textpad(collected) flushers.text(collected) for interface, whatever in next, collected do local commands = whatever.commands local environments = whatever.environments -- -- plugin, this is a rewrite of a file provided by Lukas Prochazka -- local function merge(templatedata,destinationdata,categories) report("loading '%s'",templatedata) local data = io.loaddata(templatedata) local done = 0 for i=1,#categories do local category = categories[i] local cpattern = ";%s*category:%s*(" .. category .. ")%s*[\n\r]+" local fpattern = ";%s*filename:%s*(" .. "%S+" .. ")%s*[\n\r]+" data = gsub(data,cpattern..fpattern,function(category,filename) local found = resolvers.findfile(filename) or "" local blob = found ~= "" and io.loaddata(found) or "" if blob == "" then report("category: %s, filename: %s, not found",category,filename) else done = done + 1 report("category: %s, filename: %s, merged",category,filename) end return format("; category: %s\n; filename: %s\n%s\n\n",category,filename,blob) end) end if done > 0 then report("saving '%s' (%s files merged)",destinationdata,done) io.savedata(destinationdata,data) else report("skipping '%s' (no files merged)",destinationdata) end end local templatename = "textpad-context-template.txt" local templatedata = resolvers.findfile(templatename) or "" if templatedata == "" then report("unable to locate template '%s'",templatename) else merge(templatedata, "context.syn", { "tex commands","context commands" }) if environment.argument("textpad") == "latex" then merge(templatedata, "context-latex.syn", { "tex commands","context commands", "latex commands" }) end end local r = { } local c = io.loaddata("context-commands-en.txt") or "" -- sits on the same path local e = io.loaddata("context-environments-en.txt") or "" -- sits on the same path for s in gmatch(c,"\\(.-)%s") do r[#r+1] = format("\n!TEXT=%s\n\\%s\n!",s,s) end for s in gmatch(e,"\\start(.-)%s+\\stop(.-)") do r[#r+1] = format("\n!TEXT=%s (start/stop)\n\\start%s \\^\\stop%s\n!",s,s,s) end sort(r) insert(r,1,"!TCL=597,\n!TITLE=ConTeXt\n!SORT=N\n!CHARSET=DEFAULT") io.savedata("context.tcl",concat(r,"\n")) -- cleanup os.remove("context-commands-en.txt") os.remove("context-environments-en.txt") end end function scripts.interface.editor(editor,split,forcedinterfaces) require("char-def") local interfaces = forcedinterfaces or environment.files or userinterfaces if not interfaces.en then -- loaded as script so we have "cont-yes.*" as name interfaces = { "en" } end -- -- local filename = "i-context.xml" local filename = "context-en.xml" local xmlfile = resolvers.findfile(filename) or "" if xmlfile == "" then report("unable to locate %a",filename) return end -- local filename = "mult-def.lua" local deffile = resolvers.findfile(filename) or "" if deffile == "" then report("unable to locate %a",filename) return end local interface = dofile(deffile) if not interface or not next(interface) then report("invalid file %a",filename) return end local variables = interface.variables local constants = interface.constants local commands = interface.commands local elements = interface.elements -- local collected = { } -- report("generating files for %a",editor) report("loading %a",xmlfile) local xmlroot = xml.load(xmlfile) -- xml.include(xmlroot,"cd:interfacefile","filename",true,function(s) -- local fullname = resolvers.findfile(s) -- if fullname and fullname ~= "" then -- report("including %a",fullname) -- return io.loaddata(fullname) -- end -- end) -- local definitions = { } -- for e in xml.collected(xmlroot,"cd:interface/cd:define") do -- definitions[e.at.name] = e.dt -- end -- local function resolve(root) -- for e in xml.collected(root,"*") do -- if e.tg == "resolve" then -- local resolved = definitions[e.at.name or ""] -- if resolved then -- -- use proper replace helper -- e.__p__.dt[e.ni] = resolved -- resolved.__p__ = e.__p__ -- resolve(resolved) -- end -- end -- end -- end -- resolve(xmlroot) -- todo: use sequence for i=1,#interfaces do local interface = interfaces[i] local i_commands = { } local i_environments = { } local start = elements.start[interface] or elements.start.en local stop = elements.stop [interface] or elements.stop .en for e in xml.collected(xmlroot,"cd:interface/cd:command") do local at = e.at local name = at["name"] or "" local type = at["type"] if name ~= "" then local c = commands[name] local n = c and (c[interface] or c.en) or name local sequence = xml.all(e,"/cd:sequence/*") if at.generated == "yes" then for e in xml.collected(e,"/cd:instances/cd:constant") do local name = e.at.value if name then local c = variables[name] local n = c and (c[interface] or c.en) or name if sequence then local done = { } for i=1,#sequence do local e = sequence[i] if e.tg == "string" then local value = e.at.value if value then done[i] = value else done = false break end else local tg = e.tg if tg == "instance" or tg == "instance:assignment" or tg == "instance:ownnumber" then done[i] = name else done = false break end end end if done then n = concat(done) end end if type ~= "environment" then i_commands[n] = true elseif split then i_environments[n] = true else i_commands[start..n] = true i_commands[stop ..n] = true end end end -- skip (for now) elseif type ~= "environment" then i_commands[n] = at.file or true elseif split then i_environments[n] = at.file or true else -- variables ? i_commands[start..n] = at.file or true i_commands[stop ..n] = at.file or true end end end if next(i_commands) then collected[interface] = { commands = i_commands, environments = i_environments, } end end -- local commoncommands = { } local commonenvironments = { } for k, v in next, collected do local c = v.commands local e = v.environments if k == "en" then for k, v in next, c do commoncommands[k] = true end for k, v in next, e do commonenvironments[k] = true end else for k, v in next, c do if not commoncommands[k] then commoncommands[k] = nil end end for k, v in next, e do if not commonenvironments[k] then commonenvironments[k] = nil end end end end local filenames = { } if collected.en then for k, v in next, collected.en.commands do if v ~= true then filenames[k] = v end end for k, v in next, collected.en.environments do if v ~= true then filenames[k] = v end end end for k, v in next, collected do local c = v.commands local e = v.environments for k, v in next, commoncommands do c[k] = nil end for k, v in next, commonenvironments do e[k] = nil end v.commands = sortedkeys(c) v.environments = sortedkeys(e) end -- local mathnames = { } local textnames = { } for k, v in next, characters.data do local name = v.contextname if name then textnames[name] = true end local name = v.mathname if name then mathnames[name] = true end local spec = v.mathspec if spec then for i=1,#spec do local s = spec[i] local name = s.name if name then mathnames[name] = true end end end end -- collected.common = { textnames = sortedkeys(textnames), mathnames = sortedkeys(mathnames), commands = sortedkeys(commoncommands), environments = sortedkeys(commonenvironments), filenames = filenames, } -- local flusher = flushers[editor] if flusher then return flusher(collected) end end function scripts.interface.check() local xmlfile = resolvers.findfile("cont-en.xml") or "" if xmlfile ~= "" then local f = io.open("cont-en-check.tex","w") if f then f:write("\\starttext\n") local x = xml.load(xmlfile) for e, d, k in xml.elements(x,"/cd:interface/cd:command") do local dk = d[k] local at = dk.at if at then local name = xml.filter(dk,"cd:sequence/cd:string/attribute(value)") if name and name ~= "" then if at.type == "environment" then name = "start" .. name end f:write(format("\\doifundefined{%s}{\\writestatus{check}{command '%s' is undefined}}\n",name,name)) end end end f:write("\\stoptext\n") f:close() end end end function scripts.interface.mkii() local filename = resolvers.findfile(environment.files[1] or "mult-def.lua") or "" if filename ~= "" then local interface = dofile(filename) if interface and next(interface) then local variables, constants, commands, elements = interface.variables, interface.constants, interface.commands, interface.elements local filename = resolvers.findfile("cont-en.xml") or "" local xmldata = filename ~= "" and (io.loaddata(filename) or "") local function flush(texresult,xmlresult,language,what,tag) local t = interface[what] texresult[#texresult+1] = format("%% definitions for interface %s for language %s\n%%",what,language) xmlresult[#xmlresult+1] = format("\t\n",what,language) xmlresult[#xmlresult+1] = format("\t",what) local sorted = sortedkeys(t) for i=1,#sorted do local key = sorted[i] local v = t[key] local value = v[language] or v["en"] if not value then report("warning, no value for key '%s' for language '%s'",key,language) else local value = t[key][language] or t[key].en texresult[#texresult+1] = format("\\setinterface%s{%s}{%s}",tag,key,value) xmlresult[#xmlresult+1] = format("\t\t",tag,key,value) end end xmlresult[#xmlresult+1] = format("\t\n",what) end local function replace(str, element, attribute, category, othercategory, language) return str:gsub(format("(<%s[^>]-%s=)([\"\'])([^\"\']-)([\"\'])",element,attribute), function(a,b,c) local cc = category[c] if not cc and othercategory then cc = othercategory[c] end if cc then ccl = cc[language] if ccl then return a .. b .. ccl .. b end end return a .. b .. c .. b end) end -- we could just replace attributes for language, _ in next, commands.setuplayout do -- keyword files local texresult, xmlresult = { }, { } texresult[#texresult+1] = format("%% this file is auto-generated, don't edit this file\n%%") xmlresult[#xmlresult+1] = format("\n",tag) xmlresult[#xmlresult+1] = format("\n",language) flush(texresult,xmlresult,language,"variables","variable") flush(texresult,xmlresult,language,"constants","constant") flush(texresult,xmlresult,language,"elements", "element") flush(texresult,xmlresult,language,"commands", "command") texresult[#texresult+1] = format("%%\n\\endinput") xmlresult[#xmlresult+1] = format("") local texfilename = format("mult-%s.mkii",language) local xmlfilename = format("keys-%s.xml",language) io.savedata(texfilename,concat(texresult,"\n")) report("saving interface definitions '%s'",texfilename) io.savedata(xmlfilename,concat(xmlresult,"\n")) report("saving interface translations '%s'",xmlfilename) -- mkii files if language ~= "en" and xmldata ~= "" then local newdata = xmldata:gsub("( 0 then report("+ %5i : %s => %s",v,k,contextnames[k]) else report("- %5i : %s",-v,k,contextnames[k]) end end filename = filename .. ".toutf" report("saving %a",filename) io.savedata(filename,str) end end function scripts.interface.meaning() local runner = "mtxrun --silent --script context --extra=meaning --once --noconsole --nostatistics" local pattern = environment.arguments.pattern local files = environment.files if type(pattern) == "string" then runner = runner .. ' --pattern="' .. pattern .. '"' elseif files and #files > 0 then for i=1,#files do runner = runner .. ' "' .. files[i] .. '"' end else return end local r = os.resultof(runner) if type(r) == "string" then r = gsub(r,"^.-(meaning%s+>)","\n%1") print(r) end end function scripts.interface.tokens() local runner = "mtxrun --silent --script context --extra=meaning --tokens --once --noconsole --nostatistics" local pattern = environment.arguments.pattern local files = environment.files if type(pattern) == "string" then runner = runner .. ' --pattern="' .. pattern .. '"' elseif files and #files > 0 then for i=1,#files do runner = runner .. ' "' .. files[i] .. '"' end else return end local r = os.resultof(runner) if type(r) == "string" then r = gsub(r,"^.-(tokens%s+>)","\n%1") print(r) end end local ea = environment.argument if ea("mkii") then scripts.interface.mkii() elseif ea("preprocess") then scripts.interface.preprocess() elseif ea("meaning") then scripts.interface.meaning() elseif ea("tokens") then scripts.interface.tokens() elseif ea("toutf") then scripts.interface.toutf() elseif ea("bidi") then scripts.interface.bidi() elseif ea("check") then scripts.interface.check() elseif ea("scite") or ea("bbedit") or ea("jedit") or ea("textpad") or ea("vim") or ea("text") or ea("raw") then if ea("scite") then scripts.interface.editor("scite") end if ea("bbedit") then scripts.interface.editor("bbedit") end if ea("jedit") then scripts.interface.editor("jedit") end if ea("textpad") then scripts.interface.editor("textpad",true, { "en" }) end if ea("vim") then scripts.interface.editor("vim",true, { "en" }) end if ea("text") then scripts.interface.editor("text") end if ea("raw") then scripts.interface.editor("raw") end elseif ea("exporthelp") then application.export(ea("exporthelp"),environment.files[1]) else application.help() end