mtx-vscode.lua /size: 135 Kb    last modification: 2023-12-21 09:43
1if not modules then modules = { } end modules ['mtx-vscode'] = {
2    version   = 1.000,
3    comment   = "this script is experimental",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE",
6    license   = "see context related readme files"
7}
8
9-- todo: folding and comments
10-- todo: runners (awaiting global script setup)
11-- todo: dark theme
12
13-- Already for quite a while lexing in ConTeXt is kind of standardized and the way
14-- the format evolved also relates to how I see the source. We started with lexing
15-- beginning of 1990 with texedit/wdt in Modula2 and went via perltk (texwork),
16-- Scite (native), Scite (lpeg) as well as different stages in verbatim. So, as
17-- github uses VSCODE I decided to convert the couple of base lexers to the format
18-- that this editor likes. It's all about habits and consistency, not about tons of
19-- fancy features that I don't need and would not use anyway.
20--
21-- I use a lua script to generate the lexer definitions simply because that way the
22-- update will be in sync with the updates in the context distribution.
23--
24--   code.exe --extensions-dir e:\vscode\extensions --install-extension context
25--
26-- In the end all these systems look alike so again we we have these token onto
27-- styling mappings. We even have embedded lexers. Actually, when reading the
28-- explanations it has become internally more close to what scintilla does with
29-- tokens and numbers related to it but then mapped back onto css.
30--
31-- Multiline lexing is a pain here, so I just assume that stuff belonging together is
32-- on one line (like keys to simple values). I'm not in the mood for ugly multistep
33-- parsing now. Here the lpeg lexers win.
34--
35-- We can optimize these expressions if needed but it all looks fast enough. Anyway,
36-- we do start from the already old lexers that we use in SciTe. The lexing as well
37-- as use of colors is kind of consistent and standardized in context and I don't
38-- want to change it. The number of colors is not that large and (at least to me) it
39-- looks less extreme. We also use a gray background because over time we figured
40-- out that this works best (1) for long sessions, and (2) for colors. We have quite
41-- some embedding so that is another reason for consistency.
42--
43-- I do remember generating plist files many years ago but stopped doing that
44-- because I never could check them in practice. We're now kind of back to that. The
45-- reason for using a lua script to generate the json file is that it is easier to
46-- keep in sync with context and also because then a user can just generate the
47-- extension her/himself.
48--
49-- There are nice examples of lexer definitions in the vc extensions path. My regexp
50-- experiences are somewhat rusted and I don't really have intentions to spend too
51-- much time on them. Compared to the lpeg lexers the regexp based ones are often
52-- more compact. It's just a different concept. Anyway, I might improve things after
53-- I've read more of the specs (it seems like the regexp engine is the one from ruby).
54
55-- We normally use a light gray background with rather dark colors which at least
56-- for us is less tiresome. The problem with dark backgrounds is that one needs to
57-- use light colors from pastel palettes. I need to figure out a mapping that works
58-- for a dark background so that optionally one can install without color theme.
59
60-- It is possible to define tasks and even relate them to languages but for some reason
61-- it can not be done global but per workspace which makes using vscode no option for
62-- me (too many different folders with source code, documentation etc). It's kind of
63-- strange because simple runners are provided by many editors. I don't want to program
64-- a lot to get such simple things done so, awaiting global tasks I stick to using the
65-- terminal. Also, having .vscode folderd in every place where a file sits makes no
66-- sense and clutters my disk too much. There is not much progress in this area but we
67-- are prepared.
68
69-- Another showstopper is the fact that we cannot disable utf8 for languages (like pdf,
70-- which is just bytes). I couldn't figure out how to set it in the extension.
71
72-- {
73--     "window.zoomLevel": 2,
74--     "editor.renderWhitespace": "all",
75--     "telemetry.enableCrashReporter": false,
76--     "telemetry.enableTelemetry": false,
77--     "editor.fontFamily": "Dejavu Sans Mono, Consolas, 'Courier New', monospace",
78--     "window.autoDetectHighContrast": false,
79--     "zenMode.hideLineNumbers": false,
80--     "zenMode.centerLayout": false,
81--     "zenMode.fullScreen": false,
82--     "zenMode.hideTabs": false,
83--     "workbench.editor.showIcons": false,
84--     "workbench.settings.enableNaturalLanguageSearch": false,
85--     "window.enableMenuBarMnemonics": false,
86--     "search.location": "panel",
87--     "breadcrumbs.enabled": false,
88--     "workbench.activityBar.visible": false,
89--     "editor.minimap.enabled": false,
90--     "workbench.iconTheme": null,
91--     "extensions.ignoreRecommendations": true,
92--     "editor.renderControlCharacters": true,
93--     "terminal.integrated.scrollback": 5000,
94--     "workbench.colorTheme": "ConTeXt",
95--     "[context.cld]": {},
96--     "terminal.integrated.fontSize": 10,
97--     "terminal.integrated.rendererType": "dom",
98--     "workbench.colorCustomizations": {
99--         "terminal.ansiBlack":         "#000000",
100--         "terminal.ansiWhite":         "#FFFFFF",
101--         "terminal.ansiRed":           "#7F0000",
102--         "terminal.ansiGreen":         "#007F00",
103--         "terminal.ansiBlue":          "#00007F",
104--         "terminal.ansiMagenta":       "#7F007F",
105--         "terminal.ansiCyan":          "#007F7F",
106--         "terminal.ansiYellow":        "#7F7F00",
107--         "terminal.ansiBrightBlack":   "#000000",
108--         "terminal.ansiBrightWhite":   "#FFFFFF",
109--         "terminal.ansiBrightRed":     "#7F0000",
110--         "terminal.ansiBrightGreen":   "#007F00",
111--         "terminal.ansiBrightBlue":    "#00007F",
112--         "terminal.ansiBrightMagenta": "#7F007F",
113--         "terminal.ansiBrightCyan":    "#007F7F",
114--         "terminal.ansiBrightYellow":  "#7F7F00",
115--     }
116-- }
117
118-- kind of done:
119--
120--   tex mps lua cld bibtex sql bnf(untested) pdf xml json c(pp)(simplified)
121--
122-- unlikely to be done (ok, i'm not interested in all this ide stuff anyway):
123--
124--   cpp-web tex-web web web-snippets txt
125--
126-- still todo:
127--
128--   xml: preamble and dtd
129--   pdf: nested string (..(..)..)
130
131local helpinfo = [[
132<?xml version="1.0"?>
133<application>
134 <metadata>
135  <entry name="name">mtx-vscode</entry>
136  <entry name="detail">vscode extension generator</entry>
137  <entry name="version">1.00</entry>
138 </metadata>
139 <flags>
140  <category name="basic">
141   <subcategory>
142    <flag name="generate"><short>generate extension in sync with current version</short></flag>
143    <flag name="program"><short>use the given binary (e.g. codium, default: code)</short></flag>
144    <flag name="start"><short>start vscode with extension context</short></flag>
145    <flag name="lsfile"><short>generate language server file (work in progress)</short></flag>
146   </subcategory>
147  </category>
148 </flags>
149 <examples>
150  <category>
151   <title>Example</title>
152   <subcategory>
153    <example><command>mtxrun --script vscode --generate e:/vscode/extensions</command></example>
154    <example><command>mtxrun --script vscode --generate</command></example>
155    <example><command>mtxrun --script vscode --start</command></example>
156    <example><command>mtxrun --script vscode --program=codium --start</command></example>
157   </subcategory>
158  </category>
159 </examples>
160</application>
161]]
162
163local application = logs.application {
164    name     = "mtx-vscode",
165    banner   = "vscode extension generator",
166    helpinfo = helpinfo,
167}
168
169local concat = table.concat
170
171local report = application.report
172
173require("util-jsn")
174
175scripts        = scripts        or { }
176scripts.vscode = scripts.vscode or { }
177
178local readmedata = [[
179These files are generated. You can use these extensions with for instance:
180
181  code.exe --extensions-dir <someplace>/tex/texmf-context/context/data/vscode/extensions --install-extension context
182
183There are examples of scripts and keybindings too.
184]]
185
186local function locate()
187    local name = resolvers.findfile("vscode-context.readme")
188    if name and name ~= "" then
189        local path = file.dirname(file.dirname(name))
190        if lfs.isdir(path) then
191            return path
192        end
193    end
194end
195
196function scripts.vscode.generate(targetpath)
197
198    local targetpath = targetpath or environment.files[1] or locate()
199
200    if not targetpath or targetpath == "" or not lfs.isdir(targetpath) then
201        report("invalid targetpath %a",targetpath)
202        return
203    end
204
205    local contextpath = string.gsub(targetpath,"\\","/")  .. "/context"
206
207    dir.makedirs(contextpath)
208
209    if not lfs.chdir(contextpath) then
210        return
211    end
212
213    local syntaxpath     = contextpath .. "/syntaxes"
214    local themepath      = contextpath .. "/themes"
215    local taskpath       = contextpath .. "/tasks"
216    local keybindingpath = contextpath .. "/keybindings"
217    local settingspath   = contextpath .. "/settings"
218
219    dir.makedirs(syntaxpath)
220    dir.makedirs(themepath)
221    dir.makedirs(taskpath)
222    dir.makedirs(keybindingpath)
223    dir.makedirs(settingspath)
224
225    if not lfs.isdir(syntaxpath)     then return end
226    if not lfs.isdir(themepath)      then return end
227    if not lfs.isdir(taskpath)       then return end
228    if not lfs.isdir(keybindingpath) then return end
229    if not lfs.isdir(settingspath)   then return end
230
231    -- The package.
232
233    local languages    = { }
234    local grammars     = { }
235    local themes       = { }
236    local tasks        = { }
237    local keybindings  = { }
238
239    local function registerlexer(lexer)
240
241        local category    = lexer.category
242        local contextid   = "context." .. category
243        local scope       = "source." .. contextid
244
245        local setupfile   = "./settings/context-settings-" .. category .. ".json"
246        local grammarfile = "./syntaxes/context-syntax-" .. category .. ".json"
247
248        local grammar = utilities.json.tojson {
249            name       = contextid,
250            scopeName  = scope,
251            version    = lexer.version,
252            repository = lexer.repository,
253            patterns   = lexer.patterns,
254        }
255
256        local setup = utilities.json.tojson(lexer.setup)
257
258        local suffixes   = lexer.suffixes or { }
259        local extensions = { }
260
261        for i=1,#suffixes do
262            extensions[i] = "." .. string.gsub(suffixes[i],"%.","")
263        end
264
265        table.sort(extensions)
266
267        languages[#languages+1] = {
268            id            = contextid,
269            extensions    = #extensions > 0 and extensions or nil,
270            aliases       = { lexer.description },
271            configuration = setupfile,
272        }
273
274        grammars[#grammars+1] = {
275            language  = contextid,
276            scopeName = "source." .. contextid,
277            path      = grammarfile,
278        }
279
280        report("saving grammar for %a in %a",category,grammarfile)
281        report("saving setup for %a in %a",category,setupfile)
282
283        io.savedata(grammarfile, grammar)
284        io.savedata(setupfile, setup)
285
286    end
287
288 -- local function registersettings()
289 --
290 --     local filename = "./settings.json"
291 --
292 --     local data = {
293 --         ["editor.bracketPairColorization.enabled"]                            = false,
294 --         ["editor.bracketPairColorization.independentColorPoolPerBracketType"] = false,
295 --     }
296 --
297 --     report("saving settings %a",filename)
298 --
299 --     io.savedata(filename,data)
300 -- end
301
302    local function registertheme(theme)
303
304        local category = theme.category
305        local filename = "./themes/" .. category .. ".json"
306
307        themes[#themes+1] = {
308            label   = theme.description,
309            uiTheme = "vs",
310            path    = filename,
311        }
312
313        local data = utilities.json.tojson {
314            ["$schema"]     = "vscode://schemas/color-theme",
315            ["name"]        = category,
316            ["colors"]      = theme.colors,
317            ["tokenColors"] = theme.styles,
318            ["settings"]    = theme.settings,
319        }
320
321        report("saving theme %a in %a",category,filename)
322
323        io.savedata(filename,data)
324
325    end
326
327    local function registertask(task)
328
329        local category = task.category
330        local filename = "./tasks/" .. category .. ".json"
331
332        tasks[#tasks+1] = {
333            label = task.description,
334            path  = filename,
335        }
336
337        local data = utilities.json.tojson {
338            ["name"]  = category,
339            ["tasks"] = task.tasks,
340        }
341
342        report("saving task %a in %a",category,filename)
343        io.savedata(filename,data)
344
345    end
346
347    local function registerkeybinding(keybinding)
348
349        local bindings = keybinding.keybindings
350
351        if bindings then
352
353            local category = keybinding.category
354            local filename = "./keybindings/" .. category .. ".json"
355
356            report("saving keybinding %a in %a",category,filename)
357
358            io.savedata(filename,utilities.json.tojson(bindings))
359
360            for i=1,#bindings do
361                 keybindings[#keybindings+1] = bindings[i]
362            end
363
364        end
365    end
366
367    local function savepackage()
368
369        local packagefile  = "package.json"
370        local whateverfile = "package.nls.json"
371        local readmefile   = "vscode-context.readme"
372
373        local specification = utilities.json.tojson {
374            name        = "context",
375            displayName = "ConTeXt",
376            description = "ConTeXt Syntax Highlighting",
377            publisher   = "ConTeXt Development Team",
378            publisher   = "cdt",
379            version     = "1.0.0",
380            engines     = {
381                vscode = "*"
382            },
383            categories = {
384                "Programming Languages",
385                "Lexers",
386                "Syntaxes"
387            },
388            contributes = {
389                languages   = languages,
390                grammars    = grammars,
391                themes      = themes,
392                tasks       = tasks,
393                keybindings = keybindings,
394            },
395
396        }
397
398        report("saving package in %a",packagefile)
399
400        io.savedata(packagefile,specification)
401
402        local whatever = utilities.json.tojson {
403            displayName = "ConTeXt",
404            description = "Provides syntax highlighting and bracket matching in ConTeXt files.",
405        }
406
407        report("saving whatever in %a",whateverfile)
408
409        io.savedata(whateverfile,whatever)
410
411        report("saving readme in %a",readmefile)
412
413        io.savedata(readmefile,readmedata)
414
415    end
416
417    -- themes
418
419    do
420
421        local mycolors = {
422            red       = "#7F0000",
423            green     = "#007F00",
424            blue      = "#00007F",
425            cyan      = "#007F7F",
426            magenta   = "#7F007F",
427            yellow    = "#7F7F00",
428            orange    = "#B07F00",
429            white     = "#FFFFFF",
430            light     = "#CFCFCF",
431            grey      = "#808080",
432            dark      = "#4F4F4F",
433            black     = "#000000",
434            selection = "#F7F7F7",
435            logpanel  = "#E7E7E7",
436            textpanel = "#CFCFCF",
437            linepanel = "#A7A7A7",
438            tippanel  = "#444444",
439            right     = "#0000FF",
440            wrong     = "#FF0000",
441            default   = "#000000",
442            reverse   = "#FFFFFF",
443
444            -- some day a dark:
445
446--     red       = "#CC4444",
447--     green     = "#44CC44",
448--     blue      = "#4444FF",
449--     cyan      = "#55BBBB",
450--     magenta   = "#BB55BB",
451--     yellow    = "#BBBB55",
452--     orange    = "#B07F00",
453--     white     = "#FFFFFF",
454--     light     = "#CFCFCF",
455--     grey      = "#808080",
456--     dark      = "#4F4F4F",
457--     black     = "#000000",
458--     selection = "#F7F7F7",
459--     logpanel  = "#E7E7E7",
460--     textpanel = "#1E1E1E", -- VS "#101010",
461--     linepanel = "#A7A7A7",
462--     tippanel  = "#444444",
463--     right     = "#0000FF",
464--     wrong     = "#FF0000",
465--     default   = "#D4D4D4",
466--     reverse   = "#000000",
467
468        }
469
470        local colors = {
471            ["editor.background"]               = mycolors.textpanel,
472            ["editor.foreground"]               = mycolors.default,
473            ["editorLineNumber.foreground"]     = mycolors.default,
474            ["editorIndentGuide.background"]    = mycolors.textpanel,
475            ["editorBracketMatch.background"]   = mycolors.textpanel,
476            ["editorBracketMatch.border"]       = mycolors.orange,
477            ["editor.lineHighlightBackground"]  = mycolors.textpanel,
478            ["focusBorder"]                     = mycolors.default,
479
480            ["activityBar.background"]          = mycolors.default,
481
482            ["editorGutter.background"]         = mycolors.linepanel,
483            ["editorGutter.foreground"]         = mycolors.default,
484            ["editorGutter.border"]             = mycolors.reverse,
485            ["sideBarTitle.foreground"]         = mycolors.default,
486            ["sideBarSectionHeader.background"] = mycolors.linepanel,
487            ["sideBarSectionHeader.foreground"] = mycolors.default,
488
489            ["statusBar.foreground"]            = mycolors.default,
490            ["statusBar.background"]            = mycolors.linepanel,
491            ["statusBar.border"]                = mycolors.reverse,
492            ["statusBar.noFolderForeground"]    = mycolors.default,
493            ["statusBar.noFolderBackground"]    = mycolors.linepanel,
494            ["statusBar.debuggingForeground"]   = mycolors.default,
495            ["statusBar.debuggingBackground"]   = mycolors.linepanel,
496
497            ["notification.background"]         = mycolors.default,
498        }
499
500        local styles = {
501
502            { scope = "context.whitespace",              settings = { } },
503            { scope = "context.default",                 settings = { foreground = mycolors.default } },
504            { scope = "context.number",                  settings = { foreground = mycolors.cyan } },
505            { scope = "context.comment",                 settings = { foreground = mycolors.yellow } },
506            { scope = "context.keyword",                 settings = { foreground = mycolors.blue, fontStyle = "bold" } },
507            { scope = "context.string",                  settings = { foreground = mycolors.magenta } },
508            { scope = "context.error",                   settings = { foreground = mycolors.red } },
509            { scope = "context.label",                   settings = { foreground = mycolors.red, fontStyle = "bold"  } },
510            { scope = "context.nothing",                 settings = { } },
511            { scope = "context.class",                   settings = { foreground = mycolors.default, fontStyle = "bold" } },
512            { scope = "context.function",                settings = { foreground = mycolors.default, fontStyle = "bold" } },
513            { scope = "context.constant",                settings = { foreground = mycolors.cyan, fontStyle = "bold" } },
514            { scope = "context.operator",                settings = { foreground = mycolors.blue } },
515            { scope = "context.regex",                   settings = { foreground = mycolors.magenta } },
516            { scope = "context.preprocessor",            settings = { foreground = mycolors.yellow, fontStyle = "bold" } },
517            { scope = "context.tag",                     settings = { foreground = mycolors.cyan } },
518            { scope = "context.type",                    settings = { foreground = mycolors.blue } },
519            { scope = "context.variable",                settings = { foreground = mycolors.default } },
520            { scope = "context.identifier",              settings = { } },
521            { scope = "context.linenumber",              settings = { background = mycolors.linepanel } },
522            { scope = "context.bracelight",              settings = { foreground = mycolors.orange, fontStyle = "bold" } },
523            { scope = "context.bracebad",                settings = { foreground = mycolors.orange, fontStyle = "bold" } },
524            { scope = "context.controlchar",             settings = { } },
525            { scope = "context.indentguide",             settings = { foreground = mycolors.linepanel, back = colors.reverse } },
526            { scope = "context.calltip",                 settings = { foreground = mycolors.reverse, back = colors.tippanel } },
527            { scope = "context.invisible",               settings = { background = mycolors.orange } },
528            { scope = "context.quote",                   settings = { foreground = mycolors.blue, fontStyle = "bold" } },
529            { scope = "context.special",                 settings = { foreground = mycolors.blue } },
530            { scope = "context.extra",                   settings = { foreground = mycolors.yellow } },
531            { scope = "context.embedded",                settings = { foreground = mycolors.default, fontStyle = "bold" } },
532            { scope = "context.char",                    settings = { foreground = mycolors.magenta } },
533            { scope = "context.reserved",                settings = { foreground = mycolors.magenta, fontStyle = "bold" } },
534            { scope = "context.definition",              settings = { foreground = mycolors.default, fontStyle = "bold" } },
535            { scope = "context.okay",                    settings = { foreground = mycolors.dark } },
536            { scope = "context.warning",                 settings = { foreground = mycolors.orange } },
537            { scope = "context.standout",                settings = { foreground = mycolors.orange, fontStyle = "bold" } },
538            { scope = "context.command",                 settings = { foreground = mycolors.green, fontStyle = "bold" } },
539            { scope = "context.internal",                settings = { foreground = mycolors.orange, fontStyle = "bold" } },
540            { scope = "context.preamble",                settings = { foreground = mycolors.yellow } },
541            { scope = "context.grouping",                settings = { foreground = mycolors.red } },
542            { scope = "context.primitive",               settings = { foreground = mycolors.blue, fontStyle = "bold" } },
543            { scope = "context.plain",                   settings = { foreground = mycolors.dark, fontStyle = "bold" } },
544            { scope = "context.user",                    settings = { foreground = mycolors.green } },
545            { scope = "context.data",                    settings = { foreground = mycolors.cyan, fontStyle = "bold" } },
546            { scope = "context.text",                    settings = { foreground = mycolors.default } },
547
548            { scope = { "emphasis" }, settings = { fontStyle = "italic" } },
549            { scope = { "strong"   }, settings = { fontStyle = "bold"   } },
550
551            { scope = { "comment"  }, settings = { foreground = mycolors.default  } },
552            { scope = { "string"   }, settings = { foreground = mycolors.magenta } },
553
554            {
555                scope = {
556                    "constant.numeric",
557                    "constant.language.null",
558                    "variable.language.this",
559                    "support.type.primitive",
560                    "support.function",
561                    "support.variable.dom",
562                    "support.variable.property",
563                    "support.variable.property",
564                    "meta.property-name",
565                    "meta.property-value",
566                    "support.constant.handlebars"
567                },
568                settings = {
569                    foreground = mycolors.cyan,
570                }
571            },
572
573            {
574                scope = {
575                    "keyword",
576                    "storage.modifier",
577                    "storage.type",
578                    "variable.parameter"
579                },
580                settings = {
581                    foreground = mycolors.blue,
582                    fontStyle  = "bold",
583                }
584            },
585
586            {
587                scope = {
588                    "entity.name.type",
589                    "entity.other.inherited-class",
590                    "meta.function-call",
591                    "entity.other.attribute-name",
592                    "entity.name.function.shell"
593                },
594                settings = {
595                    foreground = mycolors.default,
596                }
597            },
598
599            {
600                scope = {
601                    "entity.name.tag",
602                },
603                settings = {
604                    foreground = mycolors.default,
605                }
606            },
607
608        }
609
610        registertheme {
611            category    = "context",
612            description = "ConTeXt",
613            colors      = colors,
614            styles      = styles,
615        }
616
617     -- registersettings()
618
619    end
620
621    do
622
623        local presentation = {
624            echo             = true,
625            reveal           = "always",
626            focus            = false,
627            panel            = "shared",
628            showReuseMessage = false,
629            clear            = true,
630        }
631
632        -- chcp 65001 ; ...
633
634        local tasks = {
635            {
636                group   = "build",
637                label   = "process tex file",
638                type    = "shell",
639                command =             "context     --autogenerate --autopdf ${file}",
640                windows = { command = "context.exe --autogenerate --autopdf ${file}" },
641            },
642            {
643                group   = "build",
644                label   = "check tex file",
645                type    = "shell",
646                command =             "mtxrun     --autogenerate --script check ${file}",
647                windows = { command = "mtxrun.exe --autogenerate --script check ${file}" },
648            },
649            {
650                group   = "build",
651                label   = "identify fonts",
652                type    = "shell",
653                command =             "mtxrun     --script fonts --reload --force",
654                windows = { command = "mtxrun.exe --script fonts --reload --force" },
655            },
656            {
657                group   = "build",
658                label   = "process lua file",
659                type    = "shell",
660                command =             "mtxrun     --script ${file}",
661                windows = { command = "mtxrun.exe --script ${file}" },
662            },
663        }
664
665        for i=1,#tasks do
666            local task = tasks[i]
667            if not task.windows then
668                task.windows = {  command = task.command }
669            end
670            if not task.presentation then
671                task.presentation = presentation
672            end
673        end
674
675        registertask {
676            category    = "context",
677            description = "ConTeXt Tasks",
678            tasks       = tasks,
679        }
680
681    end
682
683    do
684
685         local keybindings = {
686            {
687             -- runner  = "context --autogenerate --autopdf ${file}",
688                key     = "ctrl-F12",
689                command = "workbench.action.tasks.runTask",
690                args    = "process tex file",
691                when    = "editorTextFocus && editorLangId == context.tex",
692            },
693            {
694             -- runner  = "mtxrun --autogenerate --script check ${file}",
695                key     = "F12",
696                command = "workbench.action.tasks.runTask",
697                args    = "check tex file",
698                when    = "editorTextFocus && editorLangId == context.tex",
699            },
700            {
701             -- runner  = "mtxrun --script ${file}",
702                key     = "ctrl-F12",
703                command = "workbench.action.tasks.runTask",
704                args    = "process lua file",
705                when    = "editorTextFocus && editorLangId == context.cld",
706            }
707        }
708
709        registerkeybinding {
710            category    = "context",
711            description = "ConTeXt Keybindings",
712            keybindings = keybindings,
713        }
714
715    end
716
717    -- helpers
718
719    local function loaddefinitions(name)
720        return table.load(resolvers.findfile(name))
721    end
722
723    local escapes = {
724        ["."]  = "\\.",
725        ["-"]  = "\\-",
726        ["+"]  = "\\+",
727        ["*"]  = "\\*",
728        ['"']  = '\\"',
729        ["'"]  = "\\'",
730        ['^']  = '\\^',
731        ['$']  = '\\$',
732        ["|"]  = "\\|",
733        ["\\"] = "\\\\",
734        ["["]  = "\\[",
735        ["]"]  = "\\]",
736        ["("]  = "\\(",
737        [")"]  = "\\)",
738        ["%"]  = "\\%",
739        ["!"]  = "\\!",
740        ["&"]  = "\\&",
741        ["?"]  = "\\?",
742        ["~"]  = "\\~",
743    }
744
745    local function sorter(a,b)
746        return a > b
747    end
748
749    local function oneof(t)
750        local result = { }
751        table.sort(t,sorter)
752        for i=1,#t do
753            result[i] = string.gsub(t[i],".",escapes)
754        end
755        return concat(result,"|")
756    end
757
758    local function capture(str)
759        return "(" .. str .. ")"
760    end
761
762    local function captures(str)
763        return "\\*(" .. str .. ")\\*"
764    end
765
766    local function include(str)
767        return { include = str }
768    end
769
770    local function configuration(s)
771        if s then
772            local pairs    = s.pairs
773            local comments = s.comments
774            return {
775                brackets         = pairs,
776                autoClosingPairs = pairs,
777                surroundingPairs = pairs,
778                comments = {
779                    lineComment  = comments and comments.inline or nil,
780                    blockComment = comments and comments.display or nil,
781                },
782            }
783        else
784            return { }
785        end
786    end
787
788    -- I need to figure out a decent mapping for dark as the defaults are just
789    -- not to my taste and we also have a different categorization.
790
791    local mapping = {
792        ["context.default"]      = "text source",
793        ["context.number"]       = "constant.numeric",
794        ["context.comment"]      = "comment",
795        ["context.keyword"]      = "keyword",
796        ["context.string"]       = "string source",
797        ["context.label"]        = "meta.tag",
798        ["context.constant"]     = "support.constant",
799        ["context.operator"]     = "keyword.operator.js",
800        ["context.identifier"]   = "support.variable",
801        ["context.quote"]        = "string",
802        ["context.special"]      =      "unset",
803        ["context.extra"]        =      "unset",
804        ["context.embedded"]     = "meta.embedded",
805        ["context.reserved"]     =      "unset",
806        ["context.definition"]   = "keyword",
807        ["context.warning"]      = "invalid",
808        ["context.command"]      =      "unset",
809        ["context.grouping"]     =      "unset",
810        ["context.primitive"]    = "keyword",
811        ["context.plain"]        =      "unset",
812        ["context.user"]         =      "unset",
813        ["context.data"]         = "text source",
814        ["context.text"]         = "text source",
815    }
816
817    local function styler(namespace)
818        local done  = { }
819        local style = function(what,where)
820            if not what or not where then
821                report()
822                report("?  %-5s  %-20s  %s",namespace,what or "?",where or "?")
823                report()
824                os.exit()
825            end
826-- if mapping then
827--     what = mapping[what] or what
828-- end
829            local hash = what .. "." .. where
830            if done[hash] then
831                report("-  %-5s  %-20s  %s",namespace,what,where)
832            else
833             -- report("+  %-5s  %-20s  %s",namespace,what,where)
834                done[hash] = true
835            end
836            return hash .. "." .. namespace
837        end
838        return style, function(what,where) return { name = style(what, where) } end
839    end
840
841    local function embedded(name)
842        return { { include = "source.context." .. name } }
843    end
844
845    -- The tex lexer.
846
847    do
848
849        local interface_lowlevel   = loaddefinitions("scite-context-data-context.lua")
850        local interface_interfaces = loaddefinitions("scite-context-data-interfaces.lua")
851        local interface_tex        = loaddefinitions("scite-context-data-tex.lua")
852
853        local constants  = interface_lowlevel.constants
854        local helpers    = interface_lowlevel.helpers
855        local interfaces = interface_interfaces.common
856        local primitives = { }
857        local overloaded = { }
858
859        for i=1,#helpers do
860            overloaded[helpers[i]] = true
861        end
862        for i=1,#constants do
863            overloaded[constants[i]] = true
864        end
865
866        local function add(data)
867            for k, v in next, data do
868                if v ~= "/" and v ~= "-" then
869                    if not overloaded[v] then
870                        primitives[#primitives+1] = v
871                    end
872                    v = "normal" .. v
873                    if not overloaded[v] then
874                        primitives[#primitives+1] = v
875                    end
876                end
877            end
878        end
879
880        add(interface_tex.tex)
881        add(interface_tex.etex)
882        add(interface_tex.pdftex)
883        add(interface_tex.aleph)
884        add(interface_tex.omega)
885        add(interface_tex.luatex)
886        add(interface_tex.xetex)
887
888        local luacommands = {
889            "ctxlua", "ctxcommand", "ctxfunction",
890            "ctxlatelua", "ctxlatecommand",
891            "cldcommand", "cldcontext",
892            "luaexpr", "luascript", "luathread",
893            "directlua", "latelua",
894        }
895
896        local luaenvironments = {
897            "luacode",
898            "luasetups", "luaparameterset",
899            "ctxfunction", "ctxfunctiondefinition",
900        }
901
902        local mpscommands = {
903            "reusableMPgraphic", "usableMPgraphic",
904            "uniqueMPgraphic", "uniqueMPpagegraphic",
905            "useMPgraphic", "reuseMPgraphic",
906            "MPpositiongraphic",
907        }
908
909        local mpsenvironments_o = {
910            "MPpage"
911        }
912
913        local mpsenvironments_a = {
914            "MPcode", "useMPgraphic", "reuseMPgraphic",
915            "MPinclusions", "MPinitializations", "MPdefinitions", "MPextensions",
916            "MPgraphic", "MPcalculation",
917        }
918
919        -- clf_a-zA-z_
920
921        -- btx|xml a-z
922        -- a-z btx|xml a-z
923
924        -- mp argument {...text}
925        local function words(list)
926            table.sort(list,sorter)
927            return "\\\\(" .. concat(list,"|") .. ")" .. "(?=[^a-zA-Z])"
928        end
929
930        local function bwords(list)
931            table.sort(list,sorter)
932            return "(\\\\(" .. concat(list,"|") .. "))\\s*(\\{)"
933        end
934
935        local function ewords()
936            return "(\\})"
937        end
938
939        local function environments(list)
940            table.sort(list,sorter)
941            last = concat(list,"|")
942            if #list > 1 then
943                last = "(?:" .. last .. ")"
944            end
945            return capture("\\\\start" .. last), capture("\\\\stop" .. last)
946        end
947
948        local capturedconstants     = words(constants)
949        local capturedprimitives    = words(primitives)
950        local capturedhelpers       = words(helpers)
951        local capturedcommands      = words(interfaces)
952        local capturedmpscommands   = words(mpscommands)
953
954        local spaces                = "\\s*"
955        local identifier            = "[a-zA-Z\\_@!?\127-\255]+"
956
957        local comment               = "%.*$\\n?"
958        local ifprimitive           = "\\\\if[a-zA-Z\\_@!?\127-\255]*"
959        local csname                = "\\\\[a-zA-Z\\_@!?\127-\255]+"
960        local csprefix              = "\\\\(btx|xml)[a-z]+"
961        local cssuffix              = "\\\\[a-z]+(btx|xml)[a-z]*"
962        local csreserved            = "\\\\(\\?\\?|[a-z]\\!)[a-zA-Z\\_@!?\127-\255]+"
963
964        local luaenvironmentopen,
965              luaenvironmentclose   = environments(luaenvironments)
966        local luacommandopen,
967              luacommandclose       = environments(luacommands)
968
969        local mpsenvironmentopen_o,
970              mpsenvironmentclose_o = environments(mpsenvironments_o)
971        local mpsenvironmentopen_a,
972              mpsenvironmentclose_a = environments(mpsenvironments_a)
973
974        local argumentopen          = capture("\\{")
975        local argumentclose         = capture("\\}")
976        local argumentcontent       = capture("[^\\}]*")
977
978        local optionopen            = capture("\\[")
979        local optionclose           = capture("\\]")
980        local optioncontent         = capture("[^\\]]*")
981
982        -- not ok yet, todo: equal in settings .. but it would become quite ugly, lpeg wins here
983        -- so instead we color differently
984
985        local option                = "(?:" .. optionopen   .. optioncontent   .. optionclose   ..  ")?"
986        local argument              = "(?:" .. argumentopen .. argumentcontent .. argumentclose ..  ")?"
987
988        local mpsenvironmentopen_o  = mpsenvironmentopen_o .. spaces .. option   .. spaces .. option
989        local mpsenvironmentopen_a  = mpsenvironmentopen_a .. spaces .. argument .. spaces .. argument
990
991        local style, styled         = styler("tex")
992
993        local capturedgroupings = oneof {
994            "{", "}", "$"
995        }
996
997        local capturedextras = oneof {
998            "~", "%", "^", "&", "_",
999            "-", "+", "/",
1000            "'", "`",
1001            "\\", "|",
1002        }
1003
1004        local capturedspecials = oneof {
1005            "(", ")", "[", "]", "<", ">",
1006            "#", "=", '"',
1007        }
1008
1009        local capturedescaped = "\\\\."
1010
1011        registerlexer {
1012
1013            category    = "tex",
1014            description = "ConTeXt TEX",
1015            suffixes    = { "tex", "mkiv", "mkvi", "mkix", "mkxi", "mkil", "mkxl", "mklx" },
1016            version     = "1.0.0",
1017
1018            setup       = configuration {
1019                pairs = {
1020                    { "{", "}" },
1021                    { "[", "]" },
1022                    { "(", ")" },
1023                },
1024                comments = {
1025                    inline  = "%",
1026                },
1027            },
1028
1029            repository  = {
1030
1031                comment = {
1032                    name  = style("context.comment", "comment"),
1033                    match = comment,
1034                },
1035
1036                constant = {
1037                    name  = style("context.constant", "commands.constant"),
1038                    match = capturedconstants,
1039                },
1040
1041                ifprimitive = {
1042                    name  = style("context.primitive", "commands.if"),
1043                    match = ifprimitive,
1044                },
1045
1046                primitive = {
1047                    name  = style("context.primitive", "commands.primitive"),
1048                    match = capturedprimitives,
1049                },
1050
1051                helper = {
1052                    name  = style("context.plain", "commands.plain"),
1053                    match = capturedhelpers,
1054                },
1055
1056                command = {
1057                    name  = style("context.command", "commands.context"),
1058                    match = capturedcommands,
1059                },
1060
1061                csname = {
1062                    name  = style("context.user", "commands.user"),
1063                    match = csname,
1064                },
1065
1066                escaped = {
1067                    name  = style("context.command", "commands.escaped"),
1068                    match = capturedescaped,
1069                },
1070
1071                subsystem_prefix = {
1072                    name  = style("context.embedded", "subsystem.prefix"),
1073                    match = csprefix,
1074                },
1075
1076                subsystem_suffix = {
1077                    name  = style("context.embedded", "subsystem.suffix"),
1078                    match = cssuffix,
1079                },
1080
1081                grouping = {
1082                    name  = style("context.grouping", "symbols.groups"),
1083                    match = capturedgroupings,
1084                },
1085
1086                extra = {
1087                    name  = style("context.extra", "symbols.extras"),
1088                    match = capturedextras,
1089                },
1090
1091                special = {
1092                    name  = style("context.special", "symbols.special"),
1093                    match = capturedspecials,
1094                },
1095
1096                reserved = {
1097                    name  = style("context.reserved", "commands.reserved"),
1098                    match = csreserved,
1099                },
1100
1101                lua_environment = {
1102                    ["begin"]     = luaenvironmentopen,
1103                    ["end"]       = luaenvironmentclose,
1104                    patterns      = embedded("cld"),
1105                    beginCaptures = { ["0"] = styled("context.embedded", "lua.environment.open") },
1106                    endCaptures   = { ["0"] = styled("context.embedded", "lua.environment.close") },
1107                },
1108
1109                lua_command = {
1110                    ["begin"]     = luacommandopen,
1111                    ["end"]       = luacommandclose,
1112                    patterns      = embedded("cld"),
1113                    beginCaptures = {
1114                        ["1"] = styled("context.embedded", "lua.command.open"),
1115                        ["2"] = styled("context.grouping", "lua.command.open"),
1116                    },
1117                    endCaptures   = {
1118                        ["1"] = styled("context.grouping", "lua.command.close"),
1119                    },
1120
1121                },
1122
1123                metafun_environment_o = {
1124                    ["begin"]     = mpsenvironmentopen_o,
1125                    ["end"]       = mpsenvironmentclose_o,
1126                    patterns      = embedded("mps"),
1127                    beginCaptures = {
1128                        ["1"] = styled("context.embedded", "metafun.environment.start.o"),
1129                        ["2"] = styled("context.embedded", "metafun.environment.open.o.1"),
1130                        ["3"] = styled("context.warning", "metafun.environment.content.o.1"),
1131                        ["4"] = styled("context.embedded", "metafun.environment.close.o.1"),
1132                        ["5"] = styled("context.embedded", "metafun.environment.open.o.2"),
1133                        ["6"] = styled("context.warning", "metafun.environment.content.o.2"),
1134                        ["7"] = styled("context.embedded", "metafun.environment.close.o.2"),
1135                    },
1136                    endCaptures   = {
1137                        ["0"] = styled("context.embedded", "metafun.environment.stop.o")
1138                    },
1139                },
1140
1141                metafun_environment_a = {
1142                    ["begin"]     = mpsenvironmentopen_a,
1143                    ["end"]       = mpsenvironmentclose_a,
1144                    patterns      = embedded("mps"),
1145                    beginCaptures = {
1146                        ["1"] = styled("context.embedded", "metafun.environment.start.a"),
1147                        ["2"] = styled("context.embedded", "metafun.environment.open.a.1"),
1148                        ["3"] = styled("context.warning", "metafun.environment.content.a.1"),
1149                        ["4"] = styled("context.embedded", "metafun.environment.close.a.1"),
1150                        ["5"] = styled("context.embedded", "metafun.environment.open.a.2"),
1151                        ["6"] = styled("context.warning", "metafun.environment.content.a.2"),
1152                        ["7"] = styled("context.embedded", "metafun.environment.close.a.2"),
1153                    },
1154                    endCaptures   = {
1155                        ["0"] = styled("context.embedded", "metafun.environment.stop.a")
1156                    },
1157                },
1158
1159                metafun_command = {
1160                    name  = style("context.embedded", "metafun.command"),
1161                    match = capturedmpscommands,
1162                },
1163
1164            },
1165
1166            patterns = {
1167                include("#comment"),
1168                include("#constant"),
1169                include("#lua_environment"),
1170                include("#lua_command"),
1171                include("#metafun_environment_o"),
1172                include("#metafun_environment_a"),
1173                include("#metafun_command"),
1174                include("#subsystem_prefix"),
1175                include("#subsystem_suffix"),
1176                include("#ifprimitive"),
1177                include("#helper"),
1178                include("#command"),
1179                include("#primitive"),
1180                include("#reserved"),
1181                include("#csname"),
1182                include("#escaped"),
1183                include("#grouping"),
1184                include("#special"),
1185                include("#extra"),
1186            },
1187
1188        }
1189
1190    end
1191
1192    -- The metafun lexer.
1193
1194    do
1195
1196        local metapostprimitives = { }
1197        local metapostinternals  = { }
1198        local metapostshortcuts  = { }
1199        local metapostcommands   = { }
1200
1201        local metafuninternals   = { }
1202        local metafunshortcuts   = { }
1203        local metafuncommands    = { }
1204
1205        local mergedshortcuts    = { }
1206        local mergedinternals    = { }
1207
1208        do
1209
1210            local definitions = loaddefinitions("scite-context-data-metapost.lua")
1211
1212            if definitions then
1213                metapostprimitives = definitions.primitives or { }
1214                metapostinternals  = definitions.internals  or { }
1215                metapostshortcuts  = definitions.shortcuts  or { }
1216                metapostcommands   = definitions.commands   or { }
1217            end
1218
1219            local definitions = loaddefinitions("scite-context-data-metafun.lua")
1220
1221            if definitions then
1222                metafuninternals  = definitions.internals or { }
1223                metafunshortcuts  = definitions.shortcuts or { }
1224                metafuncommands   = definitions.commands  or { }
1225            end
1226
1227            for i=1,#metapostshortcuts do
1228                mergedshortcuts[#mergedshortcuts+1] = metapostshortcuts[i]
1229            end
1230            for i=1,#metafunshortcuts do
1231                mergedshortcuts[#mergedshortcuts+1] = metafunshortcuts[i]
1232            end
1233
1234            for i=1,#metapostinternals do
1235                mergedinternals[#mergedinternals+1] = metapostinternals[i]
1236            end
1237            for i=1,#metafuninternals do
1238                mergedinternals[#mergedinternals+1] = metafuninternals[i]
1239            end
1240
1241
1242        end
1243
1244        local function words(list)
1245            table.sort(list,sorter)
1246            return "(" .. concat(list,"|") .. ")" .. "(?=[^a-zA-Z\\_@!?\127-\255])"
1247        end
1248
1249        local capturedshortcuts          = oneof(mergedshortcuts)
1250        local capturedinternals          = words(mergedinternals)
1251        local capturedmetapostcommands   = words(metapostcommands)
1252        local capturedmetafuncommands    = words(metafuncommands)
1253        local capturedmetapostprimitives = words(metapostprimitives)
1254
1255        local capturedsuffixes = oneof {
1256            "#@", "@#", "#"
1257        }
1258        local capturedspecials = oneof {
1259            "#@", "@#", "#",
1260            "(", ")", "[", "]", "{", "}",
1261            "<", ">", "=", ":",
1262            '"',
1263        }
1264        local capturedexatras = oneof {
1265            "+-+", "++",
1266            "~", "%", "^", "&",
1267            "_", "-", "+", "*", "/",
1268            "`", "'", "|", "\\",
1269        }
1270
1271        local spaces              = "\\s*"
1272        local mandatespaces       = "\\s+"
1273
1274        local identifier          = "[a-zA-Z\\_@!?\127-\255]+"
1275
1276        local decnumber           = "[\\-]?[0-9]+(\\.[0-9]+)?([eE]\\-?[0-9]+)?"
1277
1278        local comment             = "%.*$\\n?"
1279
1280        local stringopen          = "\""
1281        local stringclose         = stringopen
1282
1283        local qualifier           = "[\\.]"
1284        local optionalqualifier   = spaces .. qualifier .. spaces
1285
1286        local capturedstringopen  = capture(stringopen)
1287        local capturedstringclose = capture(stringclose)
1288
1289        local capturedlua         = capture("lua")
1290
1291        local capturedopen        = capture("\\(")
1292        local capturedclose       = capture("\\)")
1293
1294        local capturedtexopen     = capture("(?:b|verbatim)tex") .. mandatespaces
1295        local capturedtexclose    = mandatespaces .. capture("etex")
1296
1297        local texcommand          = "\\[a-zA-Z\\_@!?\127-\255]+"
1298
1299        local style, styled       = styler("mps")
1300
1301        registerlexer {
1302
1303            category    = "mps",
1304            description = "ConTeXt MetaFun",
1305            suffixes    = { "mp", "mpii", "mpiv", "mpxl" },
1306            version     = "1.0.0",
1307
1308            setup       = configuration {
1309                pairs = {
1310                    { "{", "}" },
1311                    { "[", "]" },
1312                    { "(", ")" },
1313                },
1314                comments = {
1315                    inline  = "%",
1316                },
1317            },
1318
1319            repository  = {
1320
1321                comment = {
1322                    name  = style("context.comment", "comment"),
1323                    match = comment,
1324                },
1325
1326                internal = {
1327                    name  = style("context.reserved", "internal"),
1328                    match = capturedshortcuts,
1329                },
1330
1331                shortcut = {
1332                    name  = style("context.data", "shortcut"),
1333                    match = capturedinternals,
1334                },
1335
1336                helper = {
1337                    name  = style("context.command.metafun", "helper"),
1338                    match = capturedmetafuncommands,
1339                },
1340
1341                plain = {
1342                    name  = style("context.plain", "plain"),
1343                    match = capturedmetapostcommands,
1344                },
1345
1346                primitive = {
1347                    name  = style("context.primitive", "primitive"),
1348                    match = capturedmetapostprimitives,
1349                },
1350
1351                quoted = {
1352                    name          = style("context.string", "string.text"),
1353                    ["begin"]     = stringopen,
1354                    ["end"]       = stringclose,
1355                    beginCaptures = { ["0"] = styled("context.special", "string.open") },
1356                    endCaptures   = { ["0"] = styled("context.special", "string.close") },
1357                },
1358
1359                identifier = {
1360                    name  = style("context.default", "identifier"),
1361                    match = identifier,
1362                },
1363
1364                suffix = {
1365                    name  = style("context.number", "suffix"),
1366                    match = capturedsuffixes,
1367                },
1368
1369                special = {
1370                    name  = style("context.special", "special"),
1371                    match = capturedspecials,
1372                },
1373
1374                number = {
1375                    name  = style("context.number", "number"),
1376                    match = decnumber,
1377                },
1378
1379                extra = {
1380                    name  = "context.extra",
1381                    match = capturedexatras,
1382                },
1383
1384                luacall = {
1385                    ["begin"]     = capturedlua .. spaces .. capturedopen .. spaces .. capturedstringopen,
1386                    ["end"]       = capturedstringclose .. spaces .. capturedclose,
1387                    patterns      = embedded("cld"),
1388                    beginCaptures =  {
1389                        ["1"] = styled("context.embedded", "lua.command"),
1390                        ["2"] = styled("context.special",  "lua.open"),
1391                        ["3"] = styled("context.special",  "lua.text.open"),
1392                    },
1393                    endCaptures   =  {
1394                        ["1"] = styled("context.special", "lua.text.close"),
1395                        ["2"] = styled("context.special", "lua.close"),
1396                    },
1397                },
1398
1399                -- default and embedded have the same color but differ in boldness
1400
1401                luacall_suffixed = {
1402                    name      = style("context.embedded", "luacall"),
1403                    ["begin"] = capturedlua,
1404                    ["end"]   = "(?!(" .. optionalqualifier .. identifier .. "))",
1405                    patterns  = {
1406                        {
1407                            match = qualifier,
1408                         -- name  = style("context.operator", "luacall.qualifier"),
1409                            name  = style("context.default", "luacall.qualifier"),
1410                        },
1411                    }
1412                },
1413
1414                texlike = { -- simplified variant
1415                    name  = style("context.warning","unexpected.tex"),
1416                    match = texcommand,
1417                },
1418
1419                texstuff = {
1420                    name          = style("context.string", "tex"),
1421                    ["begin"]     = capturedtexopen,
1422                    ["end"]       = capturedtexclose,
1423                    patterns      = embedded("tex"),
1424                    beginCaptures = { ["1"] = styled("context.primitive", "tex.open") },
1425                    endCaptures   = { ["1"] = styled("context.primitive", "tex.close") },
1426                },
1427
1428            },
1429
1430            patterns = {
1431                include("#comment"),
1432                include("#internal"),
1433                include("#shortcut"),
1434                include("#luacall_suffixed"),
1435                include("#luacall"),
1436                include("#helper"),
1437                include("#plain"),
1438                include("#primitive"),
1439                include("#texstuff"),
1440                include("#suffix"),
1441                include("#identifier"),
1442                include("#number"),
1443                include("#quoted"),
1444                include("#special"),
1445                include("#texlike"),
1446                include("#extra"),
1447            },
1448
1449        }
1450
1451    end
1452
1453    -- The lua lexer.
1454
1455    do
1456
1457        local function words(list)
1458            table.sort(list,sorter)
1459            return "(" .. concat(list,"|") .. ")" .. "(?=[^a-zA-Z])"
1460        end
1461
1462        local capturedkeywords = words {
1463            "and", "break", "do", "else", "elseif", "end", "false", "for", "function", -- "goto",
1464            "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true",
1465            "until", "while",
1466        }
1467
1468        local capturedbuiltin = words {
1469            "assert", "collectgarbage", "dofile", "error", "getmetatable",
1470            "ipairs", "load", "loadfile", "module", "next", "pairs",
1471            "pcall", "print", "rawequal", "rawget", "rawset", "require",
1472            "setmetatable", "tonumber", "tostring", "type", "unpack", "xpcall", "select",
1473            "string", "table", "coroutine", "debug", "file", "io", "lpeg", "math", "os", "package", "bit32", "utf8",
1474            -- todo: also extra luametatex ones
1475        }
1476
1477        local capturedconstants = words {
1478            "_G", "_VERSION", "_M", "\\.\\.\\.", "_ENV",
1479            "__add", "__call", "__concat", "__div", "__idiv", "__eq", "__gc", "__index",
1480            "__le", "__lt", "__metatable", "__mode", "__mul", "__newindex",
1481            "__pow", "__sub", "__tostring", "__unm", "__len",
1482            "__pairs", "__ipairs",
1483            "__close",
1484            "NaN",
1485           "<const>", "<toclose>",
1486        }
1487
1488        local capturedcsnames = words { -- todo: option
1489            "commands",
1490            "context",
1491         -- "ctxcmd",
1492         -- "ctx",
1493            "metafun",
1494            "metapost",
1495            "ctx[A-Za-z_]*",
1496        }
1497
1498        local capturedoperators = oneof {
1499            "+", "-", "*", "/", "%", "^",
1500            "#", "=", "<", ">",
1501            ";", ":", ",", ".",
1502            "{", "}", "[", "]", "(", ")",
1503            "|", "~", "'"
1504        }
1505
1506        local spaces                = "\\s*"
1507
1508        local identifier            = "[_\\w][_\\w0-9]*"
1509        local qualifier             = "[\\.\\:]"
1510        local optionalqualifier     = spaces .. "[\\.\\:]*" .. spaces
1511
1512        local doublequote           = "\""
1513        local singlequote           = "\'"
1514
1515        local doublecontent         = "(?:\\\\\"|[^\"])*"
1516        local singlecontent         = "(?:\\\\\'|[^\'])*"
1517
1518        local captureddouble        = capture(doublequote) .. capture(doublecontent) .. capture(doublequote)
1519        local capturedsingle        = capture(singlequote) .. capture(singlecontent) .. capture(singlequote)
1520
1521        local longcommentopen       = "--\\[\\["
1522        local longcommentclose      = "\\]\\]"
1523
1524        local longstringopen        = "\\[(=*)\\["
1525        local longstringclose       = "\\](\\2)\\]"
1526
1527        local shortcomment          = "--.*$\\n?"
1528
1529        local hexnumber             = "[\\-]?0[xX][A-Fa-f0-9]+(\\.[A-Fa-f0-9]+)?([eEpP]\\-?[A-Fa-f0-9]+)?"
1530        local decnumber             = "[\\-]?[0-9]+(\\.[0-9]+)?([eEpP]\\-?[0-9]+)?"
1531
1532        local capturedidentifier    = capture(identifier)
1533        local capturedgotodelimiter = capture("::")
1534        local capturedqualifier     = capture(qualifier)
1535        local capturedgoto          = capture("goto")
1536
1537        local style, styled         = styler("lua")
1538
1539        local lualexer = {
1540
1541            category    = "lua",
1542            description = "ConTeXt Lua",
1543         -- suffixes    = { "lua", "luc", "cld", "tuc", "luj", "lum", "tma", "lfg", "luv", "lui" },
1544            version     = "1.0.0",
1545
1546            setup       = configuration {
1547                pairs = {
1548                    { "(", ")" },
1549                    { "{", "}" },
1550                    { "[", "]" },
1551                },
1552                comments = {
1553                    inline  = "--",
1554                    display = { "--[[", "]]" },
1555                },
1556            },
1557
1558            repository  = {
1559
1560                shortcomment = {
1561                    name  = style("context.comment", "comment.short"),
1562                    match = shortcomment,
1563                },
1564
1565                longcomment = {
1566                    name      = style("context.comment", "comment.long"),
1567                    ["begin"] = longcommentopen,
1568                    ["end"]   = longcommentclose,
1569                },
1570
1571                keyword = {
1572                    name  = style("context.keyword", "reserved.keyword"),
1573                    match = capturedkeywords,
1574                },
1575
1576                builtin = {
1577                    name  = style("context.plain", "reserved.builtin"),
1578                    match = capturedbuiltin,
1579                },
1580
1581                constant = {
1582                    name  = style("context.data", "reserved.constants"),
1583                    match = capturedconstants,
1584                },
1585
1586                csname = {
1587                    name      = style("context.user", "csname"),
1588                    ["begin"] = capturedcsnames,
1589                    ["end"]   = "(?!(" .. optionalqualifier .. identifier .. "))",
1590                    patterns  = {
1591                        {
1592                            match = qualifier,
1593                            name  = style("context.operator", "csname.qualifier")
1594                        },
1595                    }
1596                },
1597
1598                identifier_keyword = {
1599                    match  = spaces .. capturedqualifier .. spaces .. capturedkeywords,
1600                    captures = {
1601                        ["1"] = styled("context.operator", "identifier.keyword"),
1602                        ["2"] = styled("context.warning", "identifier.keyword"),
1603                    },
1604                },
1605
1606                identifier_valid = {
1607                    name  = style("context.default", "identifier.valid"),
1608                    match = identifier,
1609                },
1610
1611                ["goto"] = {
1612                    match    = capturedgoto .. spaces .. capturedidentifier,
1613                    captures = {
1614                        ["1"] = styled("context.keyword",  "goto.keyword"),
1615                        ["2"] = styled("context.grouping", "goto.target"),
1616                    }
1617                },
1618
1619                label = {
1620                    match    = capturedgotodelimiter .. capturedidentifier .. capturedgotodelimiter,
1621                    captures = {
1622                        ["1"] = styled("context.keyword",  "label.open"),
1623                        ["2"] = styled("context.grouping", "label.target"),
1624                        ["3"] = styled("context.keyword",  "label.close"),
1625                    }
1626                },
1627
1628                operator = {
1629                    name  = style("context.special", "operator"),
1630                    match = capturedoperators,
1631                },
1632
1633                string_double = {
1634                    match    = captureddouble,
1635                    captures = {
1636                        ["1"] = styled("context.special", "doublequoted.open"),
1637                        ["2"] = styled("context.string",  "doublequoted.text"),
1638                        ["3"] = styled("context.special", "doublequoted.close"),
1639                    },
1640                },
1641
1642                string_single = {
1643                    match    = capturedsingle,
1644                    captures = {
1645                        ["1"] = styled("context.special", "singlequoted.open"),
1646                        ["2"] = styled("context.string",  "singlequoted.text"),
1647                        ["3"] = styled("context.special", "singlequoted.close"),
1648                    },
1649                },
1650
1651                string_long = {
1652                    name          = style("context.string", "long.text"),
1653                    ["begin"]     = longstringopen,
1654                    ["end"]       = longstringclose,
1655                    beginCaptures = { ["0"] = styled("context.special", "string.long.open") },
1656                    endCaptures   = { ["0"] = styled("context.special", "string.long.close") },
1657                },
1658
1659                number_hex = {
1660                    name  = style("context.number", "hexnumber"),
1661                    match = hexnumber,
1662                },
1663
1664                number = {
1665                    name  = style("context.number", "decnumber"),
1666                    match = decnumber,
1667                },
1668
1669            },
1670
1671            patterns   = {
1672                include("#keyword"),
1673                include("#buildin"),
1674                include("#constant"),
1675                include("#csname"),
1676                include("#goto"),
1677                include("#number_hex"),
1678                include("#number"),
1679                include("#identifier_keyword"),
1680                include("#identifier_valid"),
1681                include("#longcomment"),
1682                include("#string_long"),
1683                include("#string_double"),
1684                include("#string_single"),
1685                include("#shortcomment"),
1686                include("#label"),
1687                include("#operator"),
1688            },
1689
1690        }
1691
1692        local texstringopen  = "\\\\!!bs"
1693        local texstringclose = "\\\\!!es"
1694        local texcommand     = "\\\\[A-Za-z\127-\255@\\!\\?_]*"
1695
1696        local cldlexer = {
1697
1698            category    = "cld",
1699            description = "ConTeXt CLD",
1700            suffixes    = { "lmt", "lua", "luc", "cld", "tuc", "luj", "lum", "tma", "lfg", "luv", "lui" },
1701            version     = lualexer.version,
1702            setup       = lualexer.setup,
1703
1704            repository  = {
1705
1706                texstring = {
1707                    name          = style("context.string", "texstring.text"),
1708                    ["begin"]     = texstringopen,
1709                    ["end"]       = texstringclose,
1710                    beginCaptures = { ["0"] = styled("context.special", "texstring.open") },
1711                    endCaptures   = { ["0"] = styled("context.special", "texstring.close") },
1712                },
1713
1714             -- texcomment = {
1715             --     -- maybe some day
1716             -- },
1717
1718                texcommand = {
1719                    name  = style("context.warning", "texcommand"),
1720                    match = texcommand
1721                },
1722
1723            },
1724
1725            patterns = {
1726                include("#texstring"),
1727             -- include("#texcomment"),
1728                include("#texcommand"),
1729            },
1730
1731        }
1732
1733        table.merge (cldlexer.repository,lualexer.repository)
1734        table.imerge(cldlexer.patterns,  lualexer.patterns)
1735
1736        registerlexer(lualexer)
1737        registerlexer(cldlexer)
1738
1739    end
1740
1741    -- The xml lexer.
1742
1743    local xmllexer, xmlconfiguration  do
1744
1745        local spaces            = "\\s*"
1746
1747        local namespace         = "(?:[-\\w.]+:)?"
1748        local name              = "[-\\w.:]+"
1749
1750        local equal             = "="
1751
1752        local elementopen       = "<"
1753        local elementclose      = ">"
1754        local elementopenend    = "</"
1755        local elementempty      = "/?"
1756        local elementnoclose    = "?:([^>]*)"
1757
1758        local entity            = "&.*?;"
1759
1760        local doublequote       = "\""
1761        local singlequote       = "\'"
1762
1763        local doublecontent     = "(?:\\\\\"|[^\"])*"
1764        local singlecontent     = "(?:\\\\\'|[^\'])*"
1765
1766        local captureddouble    = capture(doublequote) .. capture(doublecontent) .. capture(doublequote)
1767        local capturedsingle    = capture(singlequote) .. capture(singlecontent) .. capture(singlequote)
1768
1769        local capturednamespace = capture(namespace)
1770        local capturedname      = capture(name)
1771        local capturedopen      = capture(elementopen)
1772        local capturedclose     = capture(elementclose)
1773        local capturedempty     = capture(elementempty)
1774        local capturedopenend   = capture(elementopenend)
1775
1776        local cdataopen        = "<!\\[CDATA\\["
1777        local cdataclose       = "]]>"
1778
1779        local commentopen      = "<!--"
1780        local commentclose     = "-->"
1781
1782        local processingopen   = "<\\?"
1783        local processingclose  = "\\?>"
1784
1785        local instructionopen  = processingopen .. name
1786        local instructionclose = processingclose
1787
1788        local xmlopen          = processingopen .. "xml"
1789        local xmlclose         = processingclose
1790
1791        local luaopen          = processingopen .. "lua"
1792        local luaclose         = processingclose
1793
1794        local style, styled    = styler("xml")
1795
1796        registerlexer {
1797
1798            category    = "xml",
1799            description = "ConTeXt XML",
1800            suffixes    = {
1801                "xml", "xsl", "xsd", "fo", "exa", "rlb", "rlg", "rlv", "rng",
1802                "xfdf", "xslt", "dtd", "lmx", "htm", "html", "xhtml", "ctx",
1803                "export", "svg", "xul",
1804            },
1805            version     = "1.0.0",
1806
1807            setup       = configuration {
1808                comments = {
1809                    display = { "<!--", "-->" },
1810                },
1811            },
1812
1813            repository  = {
1814
1815                attribute_double = {
1816                    match    = capturednamespace .. capturedname .. spaces .. equal .. spaces .. captureddouble,
1817                    captures = {
1818                        ["1"] = styled("context.plain",    "attribute.double.namespace"),
1819                        ["2"] = styled("context.constant", "attribute.double.name"),
1820                        ["3"] = styled("context.special",  "attribute.double.open"),
1821                        ["4"] = styled("context.string",   "attribute.double.text"),
1822                        ["5"] = styled("context.special",  "attribute.double.close"),
1823                    },
1824                },
1825
1826                attribute_single = {
1827                    match    = capturednamespace .. capturedname .. spaces .. equal .. spaces .. capturedsingle,
1828                    captures = {
1829                        ["1"] = styled("context.plain",    "attribute.single.namespace"),
1830                        ["2"] = styled("context.constant", "attribute.single.name"),
1831                        ["3"] = styled("context.special",  "attribute.single.open"),
1832                        ["4"] = styled("context.string",   "attribute.single.text"),
1833                        ["5"] = styled("context.special",  "attribute.single.close"),
1834                    },
1835                },
1836
1837                attributes = {
1838                    patterns = {
1839                        include("#attribute_double"),
1840                        include("#attribute_single"),
1841                    }
1842                },
1843
1844                entity = {
1845                    name  = style("context.constant", "entity"),
1846                    match = entity,
1847                },
1848
1849                instruction = {
1850                    name          = style("context.default", "instruction.text"),
1851                    ["begin"]     = instructionopen,
1852                    ["end"]       = instructionclose,
1853                    beginCaptures = { ["0"] = styled("context.command", "instruction.open") },
1854                    endCaptures   = { ["0"] = styled("context.command", "instruction.close") },
1855                },
1856
1857                instruction_xml = {
1858                    ["begin"]     = xmlopen,
1859                    ["end"]       = xmlclose,
1860                    beginCaptures = { ["0"] = styled("context.command", "instruction.xml.open") },
1861                    endCaptures   = { ["0"] = styled("context.command", "instruction.xml.close") },
1862                    patterns      = { include("#attributes") }
1863                },
1864
1865                instruction_lua = {
1866                    ["begin"]     = luaopen,
1867                    ["end"]       = luaclose,
1868                    patterns      = embedded("cld"),
1869                    beginCaptures = { ["0"] = styled("context.command", "instruction.lua.open") },
1870                    endCaptures   = { ["0"] = styled("context.command", "instruction.lua.close") },
1871                },
1872
1873                cdata = {
1874                    name          = style("context.default", "cdata.text"),
1875                    ["begin"]     = cdataopen,
1876                    ["end"]       = cdataclose,
1877                    beginCaptures = { ["0"] = styled("context.command", "cdata.open") },
1878                    endCaptures   = { ["0"] = styled("context.command", "cdata.close") },
1879                },
1880
1881                comment = {
1882                    name          = style("context.comment", "comment.text"),
1883                    ["begin"]     = commentopen,
1884                    ["end"]       = commentclose,
1885                    beginCaptures = { ["0"] = styled("context.command", "comment.open") },
1886                    endCaptures   = { ["0"] = styled("context.command", "comment.close") },
1887                },
1888
1889                open = {
1890                    ["begin"]     = capturedopen .. capturednamespace .. capturedname,
1891                    ["end"]       = capturedempty .. capturedclose,
1892                    patterns      = { include("#attributes") },
1893                    beginCaptures = {
1894                        ["1"] = styled("context.keyword", "open.open"),
1895                        ["2"] = styled("context.plain",   "open.namespace"),
1896                        ["3"] = styled("context.keyword", "open.name"),
1897                    },
1898                    endCaptures   = {
1899                        ["1"] = styled("context.keyword", "open.empty"),
1900                        ["2"] = styled("context.keyword", "open.close"),
1901                    },
1902                },
1903
1904                close = {
1905                    match    = capturedopenend .. capturednamespace .. capturedname .. spaces .. capturedclose,
1906                    captures = {
1907                        ["1"] = styled("context.keyword", "close.open"),
1908                        ["2"] = styled("context.plain",   "close.namespace"),
1909                        ["3"] = styled("context.keyword", "close.name"),
1910                        ["4"] = styled("context.keyword", "close.close"),
1911                    },
1912                },
1913
1914                element_error = {
1915                    name  = style("context.error","error"),
1916                    match = elementopen .. elementnoclose .. elementclose,
1917                },
1918
1919            },
1920
1921            patterns = {
1922             -- include("#preamble"),
1923                include("#comment"),
1924                include("#cdata"),
1925             -- include("#doctype"),
1926                include("#instruction_xml"),
1927                include("#instruction_lua"),
1928                include("#instruction"),
1929                include("#close"),
1930                include("#open"),
1931                include("#element_error"),
1932                include("#entity"),
1933            },
1934
1935        }
1936
1937    end
1938
1939    -- The bibtex lexer. Again we assume the keys to be on the same line as the
1940    -- first snippet of the value.
1941
1942    do
1943
1944        local spaces            = "\\s*"
1945        local open              = "{"
1946        local close             = "}"
1947        local hash              = "#"
1948        local equal             = "="
1949        local comma             = ","
1950
1951        local doublequote       = "\""
1952        local doublecontent     = "(?:\\\\\"|[^\"])*"
1953
1954        local singlequote       = "\'"
1955        local singlecontent     = "(?:\\\\\'|[^\'])*"
1956
1957        local groupopen         = "{"
1958        local groupclose        = "}"
1959        local groupcontent      = "(?:\\\\{|\\\\}|[^\\{\\}])*"
1960
1961        local shortcut          = "@(?:string|String|STRING)"    -- enforce consistency
1962        local comment           = "@(?:comment|Comment|COMMENT)" -- enforce consistency
1963
1964        local keyword           = "[a-zA-Z0-9\\_@:\\-]+"
1965
1966        local capturedcomment   = spaces .. capture(comment) .. spaces
1967        local capturedshortcut  = spaces .. capture(shortcut) .. spaces
1968        local capturedkeyword   = spaces .. capture(keyword) .. spaces
1969        local capturedopen      = spaces .. capture(open) .. spaces
1970        local capturedclose     = spaces .. capture(close) .. spaces
1971        local capturedequal     = spaces .. capture(equal) .. spaces
1972        local capturedcomma     = spaces .. capture(comma) .. spaces
1973        local capturedhash      = spaces .. capture(hash) .. spaces
1974
1975        local captureddouble    = spaces .. capture(doublequote) .. capture(doublecontent) .. capture(doublequote) .. spaces
1976        local capturedsingle    = spaces .. capture(singlequote) .. capture(singlecontent) .. capture(singlequote) .. spaces
1977        local capturedgroup     = spaces .. capture(groupopen) .. capture(groupcontent) .. capture(groupclose) .. spaces
1978
1979        local forget            = "%.*$\\n?"
1980
1981        local style, styled     = styler("bibtex")
1982
1983        registerlexer {
1984
1985            category    = "bibtex",
1986            description = "ConTeXt bibTeX",
1987            suffixes    = { "bib", "btx" },
1988            version     = "1.0.0",
1989
1990            setup       = configuration {
1991                pairs = {
1992                    { "{", "}" },
1993                },
1994                comments = {
1995                    inline  = "%",
1996                },
1997            },
1998
1999            repository  = {
2000
2001                forget = {
2002                    name  = style("context.comment", "comment.comment.inline"),
2003                    match = forget,
2004                },
2005
2006                comment = {
2007                    name  = style("context.comment", "comment.comment.content"),
2008                    ["begin"]     = capturedcomment .. capturedopen,
2009                    ["end"]       = capturedclose,
2010                    beginCaptures = {
2011                        ["1"] = styled("context.keyword", "comment.name"),
2012                        ["2"] = styled("context.grouping", "comment.open"),
2013                    },
2014                    endCaptures   = {
2015                        ["1"] = styled("context.grouping", "comment.close"),
2016                    },
2017                },
2018
2019                -- a bit inefficient but good enough
2020
2021                string_double = {
2022                    match    = capturedkeyword .. capturedequal .. captureddouble,
2023                    captures = {
2024                        ["1"] = styled("context.command","doublequoted.key"),
2025                        ["2"] = styled("context.operator","doublequoted.equal"),
2026                        ["3"] = styled("context.special", "doublequoted.open"),
2027                        ["4"] = styled("context.text", "doublequoted.text"),
2028                        ["5"] = styled("context.special", "doublequoted.close"),
2029                    },
2030                },
2031
2032                string_single = {
2033                    match    = capturedkeyword .. capturedequal .. capturedsingle,
2034                    captures = {
2035                        ["1"] = styled("context.command","singlequoted.key"),
2036                        ["2"] = styled("context.operator","singlequoted.equal"),
2037                        ["3"] = styled("context.special", "singlequoted.open"),
2038                        ["4"] = styled("context.text", "singlequoted.text"),
2039                        ["5"] = styled("context.special", "singlequoted.close"),
2040                    },
2041                },
2042
2043                string_grouped = {
2044                    match    = capturedkeyword .. capturedequal .. capturedgroup,
2045                    captures = {
2046                        ["1"] = styled("context.command","grouped.key"),
2047                        ["2"] = styled("context.operator","grouped.equal"),
2048                        ["3"] = styled("context.operator", "grouped.open"),
2049                        ["4"] = styled("context.text", "grouped.text"),
2050                        ["5"] = styled("context.operator", "grouped.close"),
2051                    },
2052                },
2053
2054                string_value = {
2055                    match    = capturedkeyword .. capturedequal .. capturedkeyword,
2056                    captures = {
2057                        ["1"] = styled("context.command", "value.key"),
2058                        ["2"] = styled("context.operator", "value.equal"),
2059                        ["3"] = styled("context.text", "value.text"),
2060                    },
2061                },
2062
2063                string_concat = {
2064                    patterns = {
2065                        {
2066                            match    = capturedhash .. captureddouble,
2067                            captures = {
2068                                ["1"] = styled("context.operator","concat.doublequoted.concatinator"),
2069                                ["2"] = styled("context.special", "concat.doublequoted.open"),
2070                                ["3"] = styled("context.text", "concat.doublequoted.text"),
2071                                ["4"] = styled("context.special", "concat.doublequoted.close"),
2072                            }
2073                        },
2074                        {
2075                            match    = capturedhash .. capturedsingle,
2076                            captures = {
2077                                ["1"] = styled("context.operator","concat.singlequoted.concatinator"),
2078                                ["2"] = styled("context.special", "concat.singlequoted.open"),
2079                                ["3"] = styled("context.text", "concat.singlequoted.text"),
2080                                ["4"] = styled("context.special", "concat.singlequoted.close"),
2081                            },
2082                        },
2083                        {
2084                            match    = capturedhash .. capturedgroup,
2085                            captures = {
2086                                ["1"] = styled("context.operator","concat.grouped.concatinator"),
2087                                ["2"] = styled("context.operator", "concat.grouped.open"),
2088                                ["3"] = styled("context.text", "concat.grouped.text"),
2089                                ["4"] = styled("context.operator", "concat.grouped.close"),
2090                            },
2091                        },
2092                        {
2093                            match    = capturedhash .. capturedkeyword,
2094                            captured = {
2095                                ["1"] = styled("context.operator","concat.value.concatinator"),
2096                                ["2"] = styled("context.text", "concat.value.text"),
2097                            },
2098                        },
2099                    },
2100                },
2101
2102                separator = {
2103                    match = capturedcomma,
2104                    name  = style("context.operator","definition.separator"),
2105                },
2106
2107                definition = {
2108                    name      = style("context.warning","definition.error"),
2109                    ["begin"] = capturedkeyword .. capturedopen .. capturedkeyword .. capturedcomma,
2110                    ["end"]   = capturedclose,
2111                    beginCaptures = {
2112                        ["1"] = styled("context.keyword", "definition.category"),
2113                        ["2"] = styled("context.grouping", "definition.open"),
2114                        ["3"] = styled("context.warning", "definition.label.text"),
2115                        ["3"] = styled("context.operator", "definition.label.separator"),
2116                    },
2117                    endCaptures = {
2118                        ["1"] = styled("context.grouping", "definition.close"),
2119                    },
2120                    patterns  = {
2121                        include("#string_double"),
2122                        include("#string_single"),
2123                        include("#string_grouped"),
2124                        include("#string_value"),
2125                        include("#string_concat"),
2126                        include("#separator"),
2127                    },
2128                },
2129
2130                concatinator = {
2131                    match = capturedhash,
2132                    name  = style("context.operator","definition.concatinator"),
2133                },
2134
2135                shortcut = {
2136                    name      =  style("context.warning","shortcut.error"),
2137                    ["begin"] = capturedshortcut .. capturedopen,
2138                    ["end"]   = capturedclose,
2139                    beginCaptures = {
2140                        ["1"] = styled("context.keyword", "shortcut.name"),
2141                        ["2"] = styled("context.grouping", "shortcut.open"),
2142                    },
2143                    endCaptures = {
2144                        ["1"] = styled("context.grouping", "shortcut.close"),
2145                    },
2146                    patterns  = {
2147                        include("#string_double"),
2148                        include("#string_single"),
2149                        include("#string_grouped"),
2150                        include("#string_value"),
2151                        include("#string_concat"),
2152                    },
2153                },
2154
2155            },
2156
2157            patterns = {
2158                include("#forget"),
2159                include("#comment"),
2160                include("#shortcut"),
2161                include("#definition"),
2162            },
2163
2164        }
2165
2166    end
2167
2168    -- The sql lexer (only needed occasionally in documentation and so).
2169
2170    do
2171
2172        -- ANSI SQL 92 | 99 | 2003
2173
2174        local function words(list)
2175            table.sort(list,sorter)
2176            local str = concat(list,"|")
2177            return "(" .. str .. "|" .. string.upper(str) .. ")" .. "(?=[^a-zA-Z])"
2178        end
2179
2180        local capturedkeywords = words {
2181            "absolute", "action", "add", "after", "all", "allocate", "alter", "and", "any",
2182            "are", "array", "as", "asc", "asensitive", "assertion", "asymmetric", "at",
2183            "atomic", "authorization", "avg", "before", "begin", "between", "bigint",
2184            "binary", "bit", "bit_length", "blob", "boolean", "both", "breadth", "by",
2185            "call", "called", "cascade", "cascaded", "case", "cast", "catalog", "char",
2186            "char_length", "character", "character_length", "check", "clob", "close",
2187            "coalesce", "collate", "collation", "column", "commit", "condition", "connect",
2188            "connection", "constraint", "constraints", "constructor", "contains", "continue",
2189            "convert", "corresponding", "count", "create", "cross", "cube", "current",
2190            "current_date", "current_default_transform_group", "current_path",
2191            "current_role", "current_time", "current_timestamp",
2192            "current_transform_group_for_type", "current_user", "cursor", "cycle", "data",
2193            "date", "day", "deallocate", "dec", "decimal", "declare", "default",
2194            "deferrable", "deferred", "delete", "depth", "deref", "desc", "describe",
2195            "descriptor", "deterministic", "diagnostics", "disconnect", "distinct", "do",
2196            "domain", "double", "drop", "dynamic", "each", "element", "else", "elseif",
2197            "end", "equals", "escape", "except", "exception", "exec", "execute", "exists",
2198            "exit", "external", "extract", "false", "fetch", "filter", "first", "float",
2199            "for", "foreign", "found", "free", "from", "full", "function", "general", "get",
2200            "global", "go", "goto", "grant", "group", "grouping", "handler", "having",
2201            "hold", "hour", "identity", "if", "immediate", "in", "indicator", "initially",
2202            "inner", "inout", "input", "insensitive", "insert", "int", "integer",
2203            "intersect", "interval", "into", "is", "isolation", "iterate", "join", "key",
2204            "language", "large", "last", "lateral", "leading", "leave", "left", "level",
2205            "like", "local", "localtime", "localtimestamp", "locator", "loop", "lower",
2206            "map", "match", "max", "member", "merge", "method", "min", "minute", "modifies",
2207            "module", "month", "multiset", "names", "national", "natural", "nchar", "nclob",
2208            "new", "next", "no", "none", "not", "null", "nullif", "numeric", "object",
2209            "octet_length", "of", "old", "on", "only", "open", "option", "or", "order",
2210            "ordinality", "out", "outer", "output", "over", "overlaps", "pad", "parameter",
2211            "partial", "partition", "path", "position", "precision", "prepare", "preserve",
2212            "primary", "prior", "privileges", "procedure", "public", "range", "read",
2213            "reads", "real", "recursive", "ref", "references", "referencing", "relative",
2214            "release", "repeat", "resignal", "restrict", "result", "return", "returns",
2215            "revoke", "right", "role", "rollback", "rollup", "routine", "row", "rows",
2216            "savepoint", "schema", "scope", "scroll", "search", "second", "section",
2217            "select", "sensitive", "session", "session_user", "set", "sets", "signal",
2218            "similar", "size", "smallint", "some", "space", "specific", "specifictype",
2219            "sql", "sqlcode", "sqlerror", "sqlexception", "sqlstate", "sqlwarning", "start",
2220            "state", "static", "submultiset", "substring", "sum", "symmetric", "system",
2221            "system_user", "table", "tablesample", "temporary", "then", "time", "timestamp",
2222            "timezone_hour", "timezone_minute", "to", "trailing", "transaction", "translate",
2223            "translation", "treat", "trigger", "trim", "true", "under", "undo", "union",
2224            "unique", "unknown", "unnest", "until", "update", "upper", "usage", "user",
2225            "using", "value", "values", "varchar", "varying", "view", "when", "whenever",
2226            "where", "while", "window", "with", "within", "without", "work", "write", "year",
2227            "zone",
2228        }
2229
2230        -- The dialects list is taken from drupal.org with standard subtracted.
2231        --
2232        -- MySQL 3.23.x | 4.x | 5.x
2233        -- PostGreSQL 8.1
2234        -- MS SQL Server 2000
2235        -- MS ODBC
2236        -- Oracle 10.2
2237
2238        local captureddialects = words {
2239            "a", "abort", "abs", "access", "ada", "admin", "aggregate", "alias", "also",
2240            "always", "analyse", "analyze", "assignment", "attribute", "attributes", "audit",
2241            "auto_increment", "avg_row_length", "backup", "backward", "bernoulli", "bitvar",
2242            "bool", "break", "browse", "bulk", "c", "cache", "cardinality", "catalog_name",
2243            "ceil", "ceiling", "chain", "change", "character_set_catalog",
2244            "character_set_name", "character_set_schema", "characteristics", "characters",
2245            "checked", "checkpoint", "checksum", "class", "class_origin", "cluster",
2246            "clustered", "cobol", "collation_catalog", "collation_name", "collation_schema",
2247            "collect", "column_name", "columns", "command_function", "command_function_code",
2248            "comment", "committed", "completion", "compress", "compute", "condition_number",
2249            "connection_name", "constraint_catalog", "constraint_name", "constraint_schema",
2250            "containstable", "conversion", "copy", "corr", "covar_pop", "covar_samp",
2251            "createdb", "createrole", "createuser", "csv", "cume_dist", "cursor_name",
2252            "database", "databases", "datetime", "datetime_interval_code",
2253            "datetime_interval_precision", "day_hour", "day_microsecond", "day_minute",
2254            "day_second", "dayofmonth", "dayofweek", "dayofyear", "dbcc", "defaults",
2255            "defined", "definer", "degree", "delay_key_write", "delayed", "delimiter",
2256            "delimiters", "dense_rank", "deny", "derived", "destroy", "destructor",
2257            "dictionary", "disable", "disk", "dispatch", "distinctrow", "distributed", "div",
2258            "dual", "dummy", "dump", "dynamic_function", "dynamic_function_code", "enable",
2259            "enclosed", "encoding", "encrypted", "end-exec", "enum", "errlvl", "escaped",
2260            "every", "exclude", "excluding", "exclusive", "existing", "exp", "explain",
2261            "fields", "file", "fillfactor", "final", "float4", "float8", "floor", "flush",
2262            "following", "force", "fortran", "forward", "freetext", "freetexttable",
2263            "freeze", "fulltext", "fusion", "g", "generated", "granted", "grants",
2264            "greatest", "header", "heap", "hierarchy", "high_priority", "holdlock", "host",
2265            "hosts", "hour_microsecond", "hour_minute", "hour_second", "identified",
2266            "identity_insert", "identitycol", "ignore", "ilike", "immutable",
2267            "implementation", "implicit", "include", "including", "increment", "index",
2268            "infile", "infix", "inherit", "inherits", "initial", "initialize", "insert_id",
2269            "instance", "instantiable", "instead", "int1", "int2", "int3", "int4", "int8",
2270            "intersection", "invoker", "isam", "isnull", "k", "key_member", "key_type",
2271            "keys", "kill", "lancompiler", "last_insert_id", "least", "length", "less",
2272            "limit", "lineno", "lines", "listen", "ln", "load", "location", "lock", "login",
2273            "logs", "long", "longblob", "longtext", "low_priority", "m", "matched",
2274            "max_rows", "maxextents", "maxvalue", "mediumblob", "mediumint", "mediumtext",
2275            "message_length", "message_octet_length", "message_text", "middleint",
2276            "min_rows", "minus", "minute_microsecond", "minute_second", "minvalue",
2277            "mlslabel", "mod", "mode", "modify", "monthname", "more", "move", "mumps",
2278            "myisam", "name", "nesting", "no_write_to_binlog", "noaudit", "nocheck",
2279            "nocompress", "nocreatedb", "nocreaterole", "nocreateuser", "noinherit",
2280            "nologin", "nonclustered", "normalize", "normalized", "nosuperuser", "nothing",
2281            "notify", "notnull", "nowait", "nullable", "nulls", "number", "octets", "off",
2282            "offline", "offset", "offsets", "oids", "online", "opendatasource", "openquery",
2283            "openrowset", "openxml", "operation", "operator", "optimize", "optionally",
2284            "options", "ordering", "others", "outfile", "overlay", "overriding", "owner",
2285            "pack_keys", "parameter_mode", "parameter_name", "parameter_ordinal_position",
2286            "parameter_specific_catalog", "parameter_specific_name",
2287            "parameter_specific_schema", "parameters", "pascal", "password", "pctfree",
2288            "percent", "percent_rank", "percentile_cont", "percentile_disc", "placing",
2289            "plan", "pli", "postfix", "power", "preceding", "prefix", "preorder", "prepared",
2290            "print", "proc", "procedural", "process", "processlist", "purge", "quote",
2291            "raid0", "raiserror", "rank", "raw", "readtext", "recheck", "reconfigure",
2292            "regexp", "regr_avgx", "regr_avgy", "regr_count", "regr_intercept", "regr_r2",
2293            "regr_slope", "regr_sxx", "regr_sxy", "regr_syy", "reindex", "reload", "rename",
2294            "repeatable", "replace", "replication", "require", "reset", "resource",
2295            "restart", "restore", "returned_cardinality", "returned_length",
2296            "returned_octet_length", "returned_sqlstate", "rlike", "routine_catalog",
2297            "routine_name", "routine_schema", "row_count", "row_number", "rowcount",
2298            "rowguidcol", "rowid", "rownum", "rule", "save", "scale", "schema_name",
2299            "schemas", "scope_catalog", "scope_name", "scope_schema", "second_microsecond",
2300            "security", "self", "separator", "sequence", "serializable", "server_name",
2301            "setof", "setuser", "share", "show", "shutdown", "simple", "soname", "source",
2302            "spatial", "specific_name", "sql_big_result", "sql_big_selects",
2303            "sql_big_tables", "sql_calc_found_rows", "sql_log_off", "sql_log_update",
2304            "sql_low_priority_updates", "sql_select_limit", "sql_small_result",
2305            "sql_warnings", "sqlca", "sqrt", "ssl", "stable", "starting", "statement",
2306            "statistics", "status", "stddev_pop", "stddev_samp", "stdin", "stdout",
2307            "storage", "straight_join", "strict", "string", "structure", "style",
2308            "subclass_origin", "sublist", "successful", "superuser", "synonym", "sysdate",
2309            "sysid", "table_name", "tables", "tablespace", "temp", "template", "terminate",
2310            "terminated", "text", "textsize", "than", "ties", "tinyblob", "tinyint",
2311            "tinytext", "toast", "top", "top_level_count", "tran", "transaction_active",
2312            "transactions_committed", "transactions_rolled_back", "transform", "transforms",
2313            "trigger_catalog", "trigger_name", "trigger_schema", "truncate", "trusted",
2314            "tsequal", "type", "uescape", "uid", "unbounded", "uncommitted", "unencrypted",
2315            "unlisten", "unlock", "unnamed", "unsigned", "updatetext", "use",
2316            "user_defined_type_catalog", "user_defined_type_code", "user_defined_type_name",
2317            "user_defined_type_schema", "utc_date", "utc_time", "utc_timestamp", "vacuum",
2318            "valid", "validate", "validator", "var_pop", "var_samp", "varbinary", "varchar2",
2319            "varcharacter", "variable", "variables", "verbose", "volatile", "waitfor",
2320            "width_bucket", "writetext", "x509", "xor", "year_month", "zerofill",
2321        }
2322
2323        local capturedoperators = oneof {
2324            "+", "-", "*", "/",
2325            "%", "^", "!", "&", "|", "?", "~",
2326            "=", "<", ">",
2327            ";", ":", ".",
2328            "{", "}", "[", "]", "(", ")",
2329        }
2330
2331        local spaces          = "\\s*"
2332        local identifier      = "[a-zA-Z\\_][a-zA-Z0-9\\_]*"
2333
2334        local comment         = "%.*$\\n?"
2335        local commentopen     = "/\\*"
2336        local commentclose    = "\\*/"
2337
2338        local doublequote     = "\""
2339        local singlequote     = "\'"
2340        local reversequote    = "`"
2341
2342        local doublecontent   = "(?:\\\\\"|[^\"])*"
2343        local singlecontent   = "(?:\\\\\'|[^\'])*"
2344        local reversecontent  = "(?:\\\\`|[^`])*"
2345
2346        local decnumber       = "[\\-]?[0-9]+(\\.[0-9]+)?([eEpP]\\-?[0-9]+)?"
2347
2348        local captureddouble  = capture(doublequote) .. capture(doublecontent) .. capture(doublequote)
2349        local capturedsingle  = capture(singlequote) .. capture(singlecontent) .. capture(singlequote)
2350        local capturedreverse = capture(reversequote) .. capture(reversecontent) .. capture(reversequote)
2351
2352        local style, styled   = styler("sql")
2353
2354        registerlexer {
2355
2356            category    = "sql",
2357            description = "ConTeXt SQL",
2358            suffixes    = { "sql" },
2359            version     = "1.0.0",
2360
2361            setup       = configuration {
2362--                 comments = {
2363--                     inline  = "...",
2364--                     display = { "...", "..." },
2365--                 },
2366            },
2367
2368            repository  = {
2369
2370                comment_short = {
2371                    name  = style("context.comment", "comment.comment"),
2372                    match = comment,
2373                },
2374
2375                comment_long = {
2376                    name          = style("context.comment", "comment.text"),
2377                    ["begin"]     = commentopen,
2378                    ["end"]       = commentclose,
2379                    beginCaptures = { ["0"] = styled("context.command", "comment.open") },
2380                    endCaptures   = { ["0"] = styled("context.command", "comment.close") },
2381                },
2382
2383                keyword_standard = {
2384                    name  = style("context.keyword", "reserved.standard"),
2385                    match = capturedkeywords,
2386                },
2387
2388                keyword_dialect = {
2389                    name  = style("context.keyword", "reserved.dialect"),
2390                    match = captureddialects,
2391                },
2392
2393                operator = {
2394                    name  = style("context.special", "operator"),
2395                    match = capturedoperators,
2396
2397                },
2398
2399                identifier = {
2400                    name  = style("context.text", "identifier"),
2401                    match = identifier,
2402                },
2403
2404                string_double = {
2405                    match    = captureddouble,
2406                    captures = {
2407                        ["1"] = styled("context.special", "doublequoted.open"),
2408                        ["2"] = styled("context.text", "doublequoted.text"),
2409                        ["3"] = styled("context.special", "doublequoted.close"),
2410                    },
2411                },
2412
2413                string_single = {
2414                    match    = capturedsingle,
2415                    captures = {
2416                        ["1"] = styled("context.special", "singlequoted.open"),
2417                        ["2"] = styled("context.text", "singlequoted.text"),
2418                        ["3"] = styled("context.special", "singlequoted.close"),
2419                    },
2420                },
2421
2422                string_reverse = {
2423                    match    = capturedreverse,
2424                    captures = {
2425                        ["1"] = styled("context.special", "reversequoted.open"),
2426                        ["2"] = styled("context.text", "reversequoted.text"),
2427                        ["3"] = styled("context.special", "reversequoted.close"),
2428                    },
2429                },
2430
2431                number = {
2432                    name  = style("context.number", "number"),
2433                    match = decnumber,
2434                },
2435
2436            },
2437
2438            patterns = {
2439                include("#keyword_standard"),
2440                include("#keyword_dialect"),
2441                include("#identifier"),
2442                include("#string_double"),
2443                include("#string_single"),
2444                include("#string_reverse"),
2445                include("#comment_long"),
2446                include("#comment_short"),
2447                include("#number"),
2448                include("#operator"),
2449            },
2450
2451        }
2452
2453    end
2454
2455    -- The bnf lexer (only used for documentation, untested).
2456
2457    do
2458
2459        local operators = oneof {
2460            "*", "+", "-", "/",
2461            ",", ".", ":", ";",
2462            "(", ")", "<", ">", "{", "}", "[",  "]",
2463             "#", "=", "?", "@", "|", " ", "!","$", "%", "&", "\\", "^", "-", "_", "`", "~",
2464        }
2465
2466        local spaces          = "\\s*"
2467
2468        local text            = "[a-zA-Z0-9]|" .. operators
2469
2470        local doublequote     = "\""
2471        local singlequote     = "\'"
2472
2473        local termopen        = "<"
2474        local termclose       = ">"
2475        local termcontent     = "([a-zA-Z][a-zA-Z0-9\\-]*)"
2476
2477        local becomes         = "::="
2478        local extra           = "|"
2479
2480        local captureddouble  = capture(doublequote) .. capture(text) .. capture(doublequote)
2481        local capturedsingle  = capture(singlequote) .. capture(text) .. capture(singlequote)
2482        local capturedterm    = capture(termopen) .. capture(termcontent) .. capture(termclose)
2483
2484        local style, styled   = styler("bnf")
2485
2486        registerlexer {
2487
2488            category    = "bnf",
2489            description = "ConTeXt BNF",
2490            suffixes    = { "bnf" },
2491            version     = "1.0.0",
2492
2493            setup       = configuration {
2494                pairs = {
2495                    { "<", ">" },
2496                },
2497            },
2498
2499            repository  = {
2500
2501                term = {
2502                    match    = capturedterm,
2503                    captures = {
2504                        ["1"] = styled("context.command", "term.open"),
2505                        ["2"] = styled("context.text", "term.text"),
2506                        ["3"] = styled("context.command", "term.close"),
2507                    },
2508                },
2509
2510                text_single = {
2511                    match    = capturedsingle,
2512                    captures = {
2513                        ["1"] = styled("context.special", "singlequoted.open"),
2514                        ["2"] = styled("context.text", "singlequoted.text"),
2515                        ["3"] = styled("context.special", "singlequoted.close"),
2516                    },
2517                },
2518
2519                text_double = {
2520                    match    = captureddouble,
2521                    captures = {
2522                        ["1"] = styled("context.special", "doublequoted.open"),
2523                        ["2"] = styled("context.text", "doublequoted.text"),
2524                        ["3"] = styled("context.special", "doublequoted.close"),
2525                    },
2526                },
2527
2528                becomes = {
2529                    name  = style("context.operator", "symbol.becomes"),
2530                    match = becomes,
2531                },
2532
2533                extra = {
2534                    name  = style("context.extra", "symbol.extra"),
2535                    match = extra,
2536                },
2537
2538            },
2539
2540            patterns = {
2541                include("#term"),
2542                include("#text_single"),
2543                include("#text_reverse"),
2544                include("#becomes"),
2545                include("#extra"),
2546            },
2547
2548        }
2549
2550    end
2551
2552    do
2553
2554        -- A rather simple one, but consistent with the rest. I don't use an IDE or fancy
2555        -- features. No tricks for me.
2556
2557        local function words(list)
2558            table.sort(list,sorter)
2559            return "\\b(" .. concat(list,"|") .. ")\\b"
2560        end
2561
2562        local capturedkeywords = words { -- copied from cpp.lua
2563            -- c
2564            "asm", "auto", "break", "case", "const", "continue", "default", "do", "else",
2565            "extern", "false", "for", "goto", "if", "inline", "register", "return",
2566            "sizeof", "static", "switch", "true", "typedef", "volatile", "while",
2567            "restrict",
2568            -- hm
2569            "_Bool", "_Complex", "_Pragma", "_Imaginary",
2570            -- c++.
2571            "catch", "class", "const_cast", "delete", "dynamic_cast", "explicit",
2572            "export", "friend", "mutable", "namespace", "new", "operator", "private",
2573            "protected", "public", "signals", "slots", "reinterpret_cast",
2574            "static_assert", "static_cast", "template", "this", "throw", "try", "typeid",
2575            "typename", "using", "virtual"
2576        }
2577
2578        local captureddatatypes = words { -- copied from cpp.lua
2579            "bool", "char", "double", "enum", "float", "int", "long", "short", "signed",
2580            "struct", "union", "unsigned", "void"
2581        }
2582
2583        local capturedluatex = words { -- new
2584            "word", "halfword", "quarterword", "scaled", "pointer", "glueratio",
2585        }
2586
2587        local capturedmacros = words { -- copied from cpp.lua
2588            "define", "elif", "else", "endif", "error", "if", "ifdef", "ifndef", "import",
2589            "include", "line", "pragma", "undef", "using", "warning"
2590        }
2591
2592        local operators = oneof {
2593            "*", "+", "-", "/",
2594            "%", "^", "!", "&", "?", "~", "|",
2595            "=", "<", ">",
2596            ";", ":", ".",
2597            "{", "}", "[", "]", "(", ")",
2598        }
2599
2600        local spaces          = "\\s*"
2601
2602        local identifier      = "[A-Za-z_][A-Za-z_0-9]*"
2603
2604        local comment         = "//.*$\\n?"
2605        local commentopen     = "/\\*"
2606        local commentclose    = "\\*/"
2607
2608        local doublequote     = "\""
2609        local singlequote     = "\'"
2610        local reversequote    = "`"
2611
2612        local doublecontent   = "(?:\\\\\"|[^\"])*"
2613        local singlecontent   = "(?:\\\\\'|[^\'])*"
2614
2615        local captureddouble  = capture(doublequote) .. capture(doublecontent) .. capture(doublequote)
2616        local capturedsingle  = capture(singlequote) .. capture(singlecontent) .. capture(singlequote)
2617
2618        local texopen         = "/\\*tex"
2619        local texclose        = "\\*/"
2620
2621        local hexnumber       = "[\\-]?0[xX][A-Fa-f0-9]+(\\.[A-Fa-f0-9]+)?([eEpP]\\-?[A-Fa-f0-9]+)?"
2622        local decnumber       = "[\\-]?[0-9]+(\\.[0-9]+)?([eEpP]\\-?[0-9]+)?"
2623
2624        local capturedmacros  = spaces .. capture("#") .. spaces .. capturedmacros
2625
2626        local style, styled   = styler("c")
2627
2628        registerlexer {
2629
2630            category    = "cpp",
2631            description = "ConTeXt C",
2632            suffixes    = { "c", "h", "cpp", "hpp" },
2633            version     = "1.0.0",
2634
2635            setup       = configuration {
2636                pairs = {
2637                    { "{", "}" },
2638                    { "[", "]" },
2639                    { "(", ")" },
2640                },
2641            },
2642
2643            repository  = {
2644
2645                keyword = {
2646                    match = capturedkeywords,
2647                    name  = style("context.keyword","c"),
2648                },
2649
2650                datatype = {
2651                    match = captureddatatypes,
2652                    name  = style("context.keyword","datatype"),
2653                },
2654
2655                luatex = {
2656                    match = capturedluatex,
2657                    name  = style("context.command","luatex"),
2658                },
2659
2660                macro = {
2661                    match = capturedmacros,
2662                    captures = {
2663                        ["1"] = styled("context.data","macro.tag"),
2664                        ["2"] = styled("context.data","macro.name"),
2665                    }
2666                },
2667
2668                texcomment = {
2669                    ["begin"]     = texopen,
2670                    ["end"]       = texclose,
2671                    patterns      = embedded("tex"),
2672                    beginCaptures = { ["0"] = styled("context.comment", "tex.open") },
2673                    endCaptures   = { ["0"] = styled("context.comment", "tex.close") },
2674                },
2675
2676                longcomment = {
2677                    name      = style("context.comment","long"),
2678                    ["begin"] = commentopen,
2679                    ["end"]   = commentclose,
2680                },
2681
2682                shortcomment = {
2683                    name  = style("context.comment","short"),
2684                    match = comment,
2685                },
2686
2687                identifier = {
2688                    name  = style("context.default","identifier"),
2689                    match = identifier,
2690                },
2691
2692                operator = {
2693                    name  = style("context.operator","any"),
2694                    match = operators,
2695                },
2696
2697                string_double = {
2698                    match    = captureddouble,
2699                    captures = {
2700                        ["1"] = styled("context.special", "doublequoted.open"),
2701                        ["2"] = styled("context.string",  "doublequoted.text"),
2702                        ["3"] = styled("context.special", "doublequoted.close"),
2703                    },
2704                },
2705
2706                string_single = {
2707                    match    = capturedsingle,
2708                    captures = {
2709                        ["1"] = styled("context.special", "singlequoted.open"),
2710                        ["2"] = styled("context.string",  "singlequoted.text"),
2711                        ["3"] = styled("context.special", "singlequoted.close"),
2712                    },
2713                },
2714
2715                hexnumber = {
2716                    name  = style("context.number","hex"),
2717                    match = hexnumber,
2718                },
2719
2720                decnumber = {
2721                    name  = style("context.number","dec"),
2722                    match = decnumber,
2723                },
2724
2725            },
2726
2727            patterns = {
2728                include("#keyword"),
2729                include("#datatype"),
2730                include("#luatex"),
2731                include("#identifier"),
2732                include("#macro"),
2733                include("#string_double"),
2734                include("#string_single"),
2735                include("#texcomment"),
2736                include("#longcomment"),
2737                include("#shortcomment"),
2738                include("#hexnumber"),
2739                include("#decnumber"),
2740                include("#operator"),
2741            },
2742
2743        }
2744
2745    end
2746
2747    -- The pdf lexer.
2748
2749    do
2750
2751        -- we can assume no errors in the syntax
2752
2753        local spaces                  = "\\s*"
2754
2755        local reserved                = oneof { "true" ,"false" , "null" }
2756        local reference               = "R"
2757
2758        local dictionaryopen          = "<<"
2759        local dictionaryclose         = ">>"
2760
2761        local arrayopen               = "\\["
2762        local arrayclose              = "\\]"
2763
2764        local stringopen              = "\\("
2765        local stringcontent           = "(?:\\\\[\\(\\)]|[^\\(\\)])*"
2766        local stringclose             = "\\)"
2767
2768        local hexstringopen           = "<"
2769        local hexstringcontent        = "[^>]*"
2770        local hexstringclose          = ">"
2771
2772        local unicodebomb             = "feff"
2773
2774        local objectopen              = "obj"    -- maybe also ^ $
2775        local objectclose             = "endobj" -- maybe also ^ $
2776
2777        local streamopen              = "^stream$"
2778        local streamclose             = "^endstream$"
2779
2780        local name                    = "/[^\\s<>/\\[\\]\\(\\)]+"   -- no need to be more clever than this
2781        local integer                 = "[\\-]?[0-9]+"              -- no need to be more clever than this
2782        local real                    = "[\\-]?[0-9]*[\\.]?[0-9]+"  -- no need to be more clever than this
2783
2784        local capturedcardinal        = "([0-9]+)"
2785
2786        local captureddictionaryopen  = capture(dictionaryopen)
2787        local captureddictionaryclose = capture(dictionaryclose)
2788
2789        local capturedarrayopen       = capture(arrayopen)
2790        local capturedarrayclose      = capture(arrayclose)
2791
2792        local capturedobjectopen      = capture(objectopen)
2793        local capturedobjectclose     = capture(objectclose)
2794
2795        local capturedname            = capture(name)
2796        local capturedinteger         = capture(integer)
2797        local capturedreal            = capture(real)
2798        local capturedreserved        = capture(reserved)
2799        local capturedreference       = capture(reference)
2800
2801        local capturedunicode         = capture(hexstringopen) .. capture(unicodebomb) .. capture(hexstringcontent) .. capture(hexstringclose)
2802        local capturedunicode         = capture(hexstringopen) .. capture(unicodebomb) .. capture(hexstringcontent) .. capture(hexstringclose)
2803        local capturedwhatsit         = capture(hexstringopen) .. capture(hexstringcontent) .. capture(hexstringclose)
2804        local capturedstring          = capture(stringopen) .. capture(stringcontent) .. capture(stringclose)
2805
2806        local style, styled           = styler("pdf")
2807
2808        -- strings are not ok yet: there can be nested unescaped () but not critical now
2809
2810        registerlexer {
2811
2812            category    = "pdf",
2813            description = "ConTeXt PDF",
2814            suffixes    = { "pdf" },
2815            version     = "1.0.0",
2816
2817            setup       = configuration {
2818                pairs = {
2819                    { "<", ">" },
2820                    { "[", "]" },
2821                    { "(", ")" },
2822                },
2823            },
2824
2825            repository  = {
2826
2827                comment = {
2828                    name  = style("context.comment","comment"),
2829                    match = "%.*$\\n?",
2830                },
2831
2832                content = {
2833                    patterns = {
2834                        { include = "#dictionary" },
2835                        { include = "#stream" },
2836                        { include = "#array" },
2837                        {
2838                            name  = style("context.constant","object.content.name"),
2839                            match = capturedname,
2840                        },
2841                        {
2842                            match    = capturedcardinal .. spaces .. capturedcardinal .. spaces .. capturedreference,
2843                            captures = {
2844                                ["1"] = styled("context.warning","content.reference.1"),
2845                                ["2"] = styled("context.warning","content.reference.2"),
2846                                ["3"] = styled("context.command","content.reference.3"),
2847                            }
2848                        },
2849                        {
2850                            name  = style("context.number","content.real"),
2851                            match = capturedreal,
2852                        },
2853                        {
2854                            name  = style("context.number","content.integer"),
2855                            match = capturedinteger,
2856                        },
2857                        {
2858                            match    = capturedstring,
2859                            captures = {
2860                                ["1"] = styled("context.quote","content.string.open"),
2861                                ["2"] = styled("context.string","content.string.text"),
2862                                ["3"] = styled("context.quote","content.string.close"),
2863                            }
2864                        },
2865                        {
2866                            name  = style("context.number","content.reserved"),
2867                            match = capturedreserved,
2868                        },
2869                        {
2870                            match    = capturedunicode,
2871                            captures = {
2872                                ["1"] = styled("context.quote","content.unicode.open"),
2873                                ["2"] = styled("context.plain","content.unicode.bomb"),
2874                                ["3"] = styled("context.string","content.unicode.text"),
2875                                ["4"] = styled("context.quote","content.unicode.close"),
2876                            }
2877                        },
2878                        {
2879                            match    = capturedwhatsit,
2880                            captures = {
2881                                ["1"] = styled("context.quote","content.whatsit.open"),
2882                                ["2"] = styled("context.string","content.whatsit.text"),
2883                                ["3"] = styled("context.quote","content.whatsit.close"),
2884                            }
2885                        },
2886                    },
2887                },
2888
2889                object = {
2890                    ["begin"]     = capturedcardinal .. spaces .. capturedcardinal .. spaces .. capturedobjectopen,
2891                    ["end"]       = capturedobjectclose,
2892                    patterns      = { { include = "#content" } },
2893                    beginCaptures = {
2894                        ["1"] = styled("context.warning","object.1"),
2895                        ["2"] = styled("context.warning","object.2"),
2896                        ["3"] = styled("context.keyword", "object.open")
2897                    },
2898                    endCaptures   = {
2899                        ["1"] = styled("context.keyword", "object.close")
2900                    },
2901                },
2902
2903                array = {
2904                    ["begin"]     = capturedarrayopen,
2905                    ["end"]       = capturedarrayclose,
2906                    patterns      = { { include = "#content" } },
2907                    beginCaptures = { ["1"] = styled("context.grouping", "array.open") },
2908                    endCaptures   = { ["1"] = styled("context.grouping", "array.close") },
2909                },
2910
2911                dictionary = {
2912                    ["begin"]     = captureddictionaryopen,
2913                    ["end"]       = captureddictionaryclose,
2914                    beginCaptures = { ["1"] = styled("context.grouping", "dictionary.open") },
2915                    endCaptures   = { ["1"] = styled("context.grouping", "dictionary.close") },
2916                    patterns      = {
2917                        {
2918                            ["begin"]     = capturedname .. spaces,
2919                            ["end"]       = "(?=[>])",
2920                            beginCaptures = { ["1"] = styled("context.command", "dictionary.name") },
2921                            patterns = { { include = "#content" } },
2922                        },
2923                    },
2924                },
2925
2926                xref = {
2927                    ["begin"] = "xref" .. spaces,
2928                    ["end"]   = "(?=[^0-9])",
2929                    captures  = {
2930                        ["0"] = styled("context.keyword", "xref.1"),
2931                    },
2932                    patterns = {
2933                        {
2934                            ["begin"] = capturedcardinal .. spaces .. capturedcardinal .. spaces,
2935                            ["end"]   = "(?=[^0-9])",
2936                            captures  = {
2937                                ["1"] = styled("context.number", "xref.2"),
2938                                ["2"] = styled("context.number", "xref.3"),
2939                            },
2940                            patterns = {
2941                                {
2942                                    ["begin"] = capturedcardinal .. spaces .. capturedcardinal .. spaces .. "([fn])" .. spaces,
2943                                    ["end"]   = "(?=.)",
2944                                    captures = {
2945                                        ["1"] = styled("context.number", "xref.4"),
2946                                        ["2"] = styled("context.number", "xref.5"),
2947                                        ["3"] = styled("context.keyword", "xref.6"),
2948                                    },
2949                                },
2950                            },
2951                        },
2952                    },
2953                },
2954
2955                startxref = {
2956                    ["begin"] = "startxref" .. spaces,
2957                    ["end"]   = "(?=[^0-9])",
2958                    captures  = {
2959                        ["0"] = styled("context.keyword", "startxref.1"),
2960                    },
2961                    patterns = {
2962                        {
2963                            ["begin"] = capturedcardinal .. spaces,
2964                            ["end"]   = "(?=.)",
2965                            captures  = {
2966                                ["1"] = styled("context.number", "startxref.2"),
2967                            },
2968                        },
2969                    },
2970                },
2971
2972                trailer = {
2973                    name  = style("context.keyword", "trailer"),
2974                    match = "trailer",
2975                },
2976
2977                stream = {
2978                    ["begin"]     = streamopen,
2979                    ["end"]       = streamclose,
2980                    beginCaptures = { ["0"] = styled("context.keyword", "stream.open") },
2981                    endCaptures   = { ["0"] = styled("context.keyword", "stream.close") },
2982                },
2983
2984            },
2985
2986            patterns = {
2987                include("#object"),
2988                include("#comment"),
2989                include("#trailer"),
2990                include("#dictionary"), -- cheat: trailer dict
2991                include("#startxref"),
2992                include("#xref"),
2993            },
2994
2995        }
2996
2997    end
2998
2999    -- The JSON lexer. I don't want to spend time on (and mess up the lexer) with
3000    -- some ugly multistage key/value parser so we just assume that the key is on
3001    -- the same line as the colon and the value. It looks bad otherwise anyway.
3002
3003    do
3004
3005        local spaces             = "\\s*"
3006        local separator          = "\\,"
3007        local becomes            = "\\:"
3008
3009        local arrayopen          = "\\["
3010        local arrayclose         = "\\]"
3011
3012        local hashopen           = "\\{"
3013        local hashclose          = "\\}"
3014
3015        local stringopen         = "\""
3016        local stringcontent      = "(?:\\\\\"|[^\"])*"
3017        local stringclose        = stringopen
3018
3019        local reserved           = oneof { "true", "false", "null" }
3020
3021        local hexnumber          = "[\\-]?0[xX][A-Fa-f0-9]+(\\.[A-Fa-f0-9]+)?([eEpP]\\-?[A-Fa-f0-9]+)?"
3022        local decnumber          = "[\\-]?[0-9]+(\\.[0-9]+)?([eEpP]\\-?[0-9]+)?"
3023
3024        local capturedarrayopen  = capture(arrayopen)
3025        local capturedarrayclose = capture(arrayclose)
3026        local capturedhashopen   = capture(hashopen)
3027        local capturedhashclose  = capture(hashclose)
3028
3029        local capturedreserved   = capture(reserved)
3030        local capturedbecomes    = capture(becomes)
3031        local capturedseparator  = capture(separator)
3032        local capturedstring     = capture(stringopen) .. capture(stringcontent) .. capture(stringclose)
3033        local capturedhexnumber  = capture(hexnumber)
3034        local captureddecnumber  = capture(decnumber)
3035
3036        local style, styled      = styler("json")
3037
3038        registerlexer {
3039
3040            category    = "json",
3041            description = "ConTeXt JSON",
3042            suffixes    = { "json" },
3043            version     = "1.0.0",
3044
3045            setup       = configuration {
3046                pairs = {
3047                    { "{", "}" },
3048                    { "[", "]" },
3049                },
3050            },
3051
3052            repository = {
3053
3054                separator = {
3055                    name  = style("context.operator","separator"),
3056                    match = spaces .. capturedseparator,
3057                },
3058
3059                reserved = {
3060                    name  = style("context.primitive","reserved"),
3061                    match = spaces .. capturedreserved,
3062                },
3063
3064                hexnumber = {
3065                    name  = style("context.number","hex"),
3066                    match = spaces .. capturedhexnumber,
3067                },
3068
3069                decnumber = {
3070                    name  = style("context.number","dec"),
3071                    match = spaces .. captureddecnumber,
3072                },
3073
3074                string = {
3075                    match    = spaces .. capturedstring,
3076                    captures = {
3077                        ["1"] = styled("context.quote","string.open"),
3078                        ["2"] = styled("context.string","string.text"),
3079                        ["3"] = styled("context.quote","string.close"),
3080                    },
3081                },
3082
3083                kv_reserved = {
3084                    match    = capturedstring .. spaces .. capturedbecomes .. spaces .. capturedreserved,
3085                    captures = {
3086                        ["1"] = styled("context.quote",    "reserved.key.open"),
3087                        ["2"] = styled("context.text",     "reserved.key.text"),
3088                        ["3"] = styled("context.quote",    "reserved.key.close"),
3089                        ["4"] = styled("context.operator", "reserved.becomes"),
3090                        ["5"] = styled("context.primitive","reserved.value"),
3091                    }
3092                },
3093
3094                kv_hexnumber = {
3095                    match = capturedstring .. spaces .. capturedbecomes .. spaces .. capturedhexnumber,
3096                    captures = {
3097                        ["1"] = styled("context.quote",   "hex.key.open"),
3098                        ["2"] = styled("context.text",    "hex.key.text"),
3099                        ["3"] = styled("context.quote",   "hex.key.close"),
3100                        ["4"] = styled("context.operator","hex.becomes"),
3101                        ["5"] = styled("context.number",  "hex.value"),
3102                    }
3103                },
3104
3105                kv_decnumber = {
3106                    match = capturedstring .. spaces .. capturedbecomes .. spaces .. captureddecnumber,
3107                    captures = {
3108                        ["1"] = styled("context.quote",   "dec.key.open"),
3109                        ["2"] = styled("context.text",    "dec.key.text"),
3110                        ["3"] = styled("context.quote",   "dec.key.close"),
3111                        ["4"] = styled("context.operator","dec.becomes"),
3112                        ["5"] = styled("context.number",  "dec.value"),
3113                    }
3114                },
3115
3116                kv_string = {
3117                    match = capturedstring .. spaces .. capturedbecomes .. spaces .. capturedstring,
3118                    captures = {
3119                        ["1"] = styled("context.quote",   "string.key.open"),
3120                        ["2"] = styled("context.text",    "string.key.text"),
3121                        ["3"] = styled("context.quote",   "string.key.close"),
3122                        ["4"] = styled("context.operator","string.becomes"),
3123                        ["5"] = styled("context.quote",   "string.value.open"),
3124                        ["6"] = styled("context.string",  "string.value.text"),
3125                        ["7"] = styled("context.quote",   "string.value.close"),
3126                    },
3127                },
3128
3129                kv_array = {
3130                    ["begin"]     = capturedstring .. spaces .. capturedbecomes .. spaces .. capturedarrayopen,
3131                    ["end"]       = arrayclose,
3132                    beginCaptures = {
3133                        ["1"] = styled("context.quote",   "array.key.open"),
3134                        ["2"] = styled("context.text",    "array.key.text"),
3135                        ["3"] = styled("context.quote",   "array.key.close"),
3136                        ["4"] = styled("context.operator","array.becomes"),
3137                        ["5"] = styled("context.grouping","array.value.open")
3138                    },
3139                    endCaptures   = {
3140                        ["0"] = styled("context.grouping","array.value.close")
3141                    },
3142                    patterns      = { include("#content") },
3143                },
3144
3145                kv_hash = {
3146                    ["begin"]     = capturedstring .. spaces .. capturedbecomes .. spaces .. capturedhashopen,
3147                    ["end"]       = hashclose,
3148                    beginCaptures = {
3149                        ["1"] = styled("context.quote",   "hash.key.open"),
3150                        ["2"] = styled("context.text",    "hash.key.text"),
3151                        ["3"] = styled("context.quote",   "hash.key.close"),
3152                        ["4"] = styled("context.operator","hash.becomes"),
3153                        ["5"] = styled("context.grouping","hash.value.open")
3154                    },
3155                    endCaptures   = {
3156                        ["0"] = styled("context.grouping","hash.value.close")
3157                    },
3158                    patterns      = { include("#kv_content") },
3159                },
3160
3161                content = {
3162                    patterns = {
3163                        include("#string"),
3164                        include("#hexnumber"),
3165                        include("#decnumber"),
3166                        include("#reserved"),
3167                        include("#hash"),
3168                        include("#array"),
3169                        include("#separator"),
3170                    },
3171                },
3172
3173                kv_content = {
3174                    patterns = {
3175                        include("#kv_string"),
3176                        include("#kv_hexnumber"),
3177                        include("#kv_decnumber"),
3178                        include("#kv_reserved"),
3179                        include("#kv_hash"),
3180                        include("#kv_array"),
3181                        include("#separator"),
3182                    },
3183                },
3184
3185                array = {
3186                    ["begin"]     = arrayopen,
3187                    ["end"]       = arrayclose,
3188                    beginCaptures = { ["0"] = styled("context.grouping","array.open") },
3189                    endCaptures   = { ["0"] = styled("context.grouping","array.close") },
3190                    patterns      = { include("#content") },
3191                },
3192
3193                hash = {
3194                    ["begin"]     = hashopen,
3195                    ["end"]       = hashclose,
3196                    beginCaptures = { ["0"] = styled("context.grouping","hash.open") },
3197                    endCaptures   = { ["0"] = styled("context.grouping","hash.close") },
3198                    patterns      = { include("#kv_content") },
3199                },
3200
3201            },
3202
3203            patterns = {
3204                include("#content"),
3205            },
3206
3207        }
3208
3209    end
3210
3211    savepackage()
3212
3213end
3214
3215-- {name: 'inherits: \\setupframed'}
3216
3217function scripts.vscode.ls(forcedinterface)
3218
3219    local interfaces = forcedinterfaces or environment.files or userinterfaces
3220    if not interfaces.en then
3221        -- loaded as script so we have "cont-yes.*" as name
3222        interfaces = { "en" }
3223    end
3224    --
3225    local filename = "context-en.xml"
3226    local xmlfile  = resolvers.findfile(filename) or ""
3227    if xmlfile == "" then
3228        report("unable to locate %a",filename)
3229        return
3230    end
3231    --
3232    local filename = "mult-def.lua"
3233    local deffile  = resolvers.findfile(filename) or ""
3234    if deffile == "" then
3235        report("unable to locate %a",filename)
3236        return
3237    end
3238    local interface = dofile(deffile)
3239    if not interface or not next(interface) then
3240        report("invalid file %a",filename)
3241        return
3242    end
3243    local variables = interface.variables
3244    local constants = interface.constants
3245    local commands  = interface.commands
3246    local elements  = interface.elements
3247    --
3248    local collected = { }
3249    --
3250    report("loading %a",xmlfile)
3251    local xmlroot = xml.load(xmlfile)
3252
3253    local interfaces = { "en" }
3254
3255--     <cd:keywords list="yes">
3256--      <cd:inherit name="setupalign"/>
3257--     </cd:keywords>
3258
3259    local function arguments(e)
3260        local p = { }
3261        for e in xml.collected(e,"/cd:arguments/*") do
3262            local tg = e.tg
3263            if tg == "keywords" then
3264                local a = { }
3265                for e in xml.collected(e,"/*") do
3266                    a[#a+1] = {
3267                        name = e.at.type
3268                    }
3269                end
3270                p[#p+1] = {
3271                    type       = tg,
3272                    attributes = #a > 0 and a or nil,
3273                    optional   = e.at.optional == "yes" or nil
3274                }
3275            elseif tg == "assignments" then
3276                local a = { }
3277                for e in xml.collected(e,"/parameter") do
3278                 -- local c = { e.at.name, "=" }
3279                    local c = { }
3280                    for e in xml.collected(e,"/constant") do
3281                        c[#c+1] = e.at.type
3282                    end
3283                 -- if #c > 0 then
3284                 --     a[#a+1] = {
3285                 --         name = concat(c, " ") -- maybe "|"
3286                 --     }
3287                 -- end
3288                    a[#a+1] = {
3289                        name = e.at.name .. "=" .. concat(c, " ") -- maybe "|"
3290                    }
3291                end
3292                p[#p+1] = {
3293                    type       = tg,
3294                    attributes = #a > 0 and a or nil,
3295                    optional   = e.at.optional == "yes" or nil
3296                }
3297            else -- e.g. "content"
3298                p[#p+1] = {
3299                    type     = tg,
3300                    optional = e.at.optional == "yes" or nil
3301                }
3302            end
3303        end
3304        return p
3305    end
3306
3307    local function details(e, f)
3308        local d = { "\\" .. f }
3309        local n = 0
3310        for e in xml.collected(e,"/cd:arguments/*") do
3311            local tg = e.tg
3312            if tg == "keywords" then
3313                n = n + 1
3314                if e.at.optional == "yes" then
3315                    d[#d+1] = "[optional " .. n .. ":..,..]"
3316                else
3317                    d[#d+1] = "[mandate "  .. n .. ":..,..]"
3318                end
3319            elseif tg == "assignments" then
3320                n = n + 1
3321                if e.at.optional == "yes" then
3322                    d[#d+1] = "[optional " .. n .. ":key=val,key=val,..]"
3323                else
3324                    d[#d+1] = "[mandate "  .. n .. ":key=val,key=val,..]"
3325                end
3326            else
3327                d[#d+1] = "{ content }"
3328            end
3329        end
3330        return concat(d, " ")
3331    end
3332
3333    -- this one is a bit weird as it could be assembled in the languages server on the fly and
3334    -- it bloats the file
3335
3336    local function documentation(c)
3337        local d = { c.detail }
3338        local p = c.params
3339        if p then
3340            local n = 0
3341            for i=1,#p do
3342                local pi = p[i]
3343                local ti = pi.type
3344                local ai = pi.attributes
3345                if ti == "keywords" then
3346                    n = n + 1
3347                    if pi.optional then
3348                        d[#d+1] = "[optional keywords " .. n .. "]"
3349                    else
3350                        d[#d+1] = "[mandate  keywords " .. n .. "]"
3351                    end
3352                    if ai then
3353                        local t = { }
3354                        for j=1,#ai do
3355                            t[#t+1] = ai[j].name
3356                        end
3357                        if #t > 0 then
3358                            d[#d+1] = concat(t," ")
3359                        end
3360                    end
3361                elseif ti == "assignments" then
3362                    n = n + 1
3363                    if pi.optional then
3364                        d[#d+1] = "[optional assignments " .. n .. "]"
3365                    else
3366                        d[#d+1] = "[mandate  assignments " .. n .. "]"
3367                    end
3368                    if ai then
3369                        local t = { }
3370                        for j=1,#ai do
3371                            t[#t+1] = ai[j].name
3372                        end
3373                        if #t > 0 then
3374                            d[#d+1] = concat(t,"\n")
3375                        end
3376                    end
3377                else
3378                    if pi.optional then
3379                        d[#d+1] = "{ optional content }"
3380                    else
3381                        d[#d+1] = "{ mandate content }"
3382                    end
3383                end
3384            end
3385        end
3386        c.documentation = concat(d,"\n")
3387--         inspect(c.documentation)
3388    end
3389
3390    if not xml.expand then
3391        -- will be in next version
3392        function xml.expand(root,pattern,whatever)
3393            local collected = xml.applylpath(root,pattern)
3394            if collected then
3395                for c=1,#collected do
3396                    local e = collected[c]
3397                    local p = e.__p__
3398                    if p then
3399                        local d = p.dt
3400                        local n = e.ni
3401                        local t = whatever(e,p)
3402                        if type(t) == "table" then
3403                            d[n] = t[1]
3404                            for i=2,#t do
3405                                n = n + 1
3406                                table.insert(d,n,t[i])
3407                            end
3408                        elseif t then
3409                            d[n] = t
3410                        end
3411                    end
3412                end
3413            end
3414        end
3415    end
3416
3417    do
3418        local c = { }
3419        xml.expand(xmlroot,"/cd:interface/cd:interface/cd:command/**/inherit",function(e)
3420            local f = c[e.at.name]
3421            if not f then
3422                f = xml.first(xmlroot,"/cd:interface/cd:interface/cd:command[@name='" .. e.at.name .. "']/cd:arguments")
3423                c[e.at.name] = f
3424            end
3425            return f and f.dt
3426        end)
3427    end
3428
3429    for i=1,#interfaces do
3430        local interface = interfaces[i]
3431        local start = elements.start[interface] or elements.start.en
3432        local stop  = elements.stop [interface] or elements.stop .en
3433        for e in xml.collected(xmlroot,"cd:interface/cd:command") do
3434            local at   = e.at
3435            local name = at["name"] or ""
3436            local type = at["type"]
3437            if name ~= "" then
3438                local c = commands[name]
3439                local n = (c and (c[interface] or c.en)) or c or name
3440                local sequence = xml.all(e,"/cd:sequence/*")
3441                if at.generated == "yes" then
3442                    -- skip (for now)
3443                elseif type ~= "environment" then
3444                    collected[#collected+1] = {
3445                        name   = n,
3446                        detail = details(e, n),
3447                        params = arguments(e), -- why not "parameters"
3448                    }
3449                else
3450                    local f = start .. n
3451                    collected[#collected+1] = {
3452                        name   = f,
3453                        start  = f,
3454                        stop   = stop  .. n,
3455                        detail = details(e, f),
3456                        params = arguments(e), -- why not "parameters"
3457                    }
3458                end
3459            end
3460        end
3461    end
3462    for i=1,#collected do
3463        documentation(collected[i])
3464    end
3465    local jsonname = "vscode-context-ls.json"
3466 -- local exmlname = "vscode-context-ls.xml"
3467    report("")
3468    report("vscode ls file saved: %s",jsonname)
3469    report("")
3470    io.savedata(jsonname,utilities.json.tojson(collected))
3471 -- io.savedata(exmlname,tostring(xmlroot))
3472end
3473
3474function scripts.vscode.start()
3475    local path = locate()
3476    if path then
3477        local codecmd = environment.arguments.program or "code" -- can be codium
3478     -- local command = 'start "vs code context" ' .. codecmd .. ' --reuse-window --ignore-gpu-blacklist --extensions-dir "' .. path .. '" --install-extension context'
3479     -- local command = 'start "vs code context" ' .. codecmd .. ' --reuse-window --extensions-dir "' .. path .. '" --install-extension context'
3480        local command = 'start "vs code context" ' .. codecmd .. ' --reuse-window --extensions-dir "' .. path .. '" --verbose --force --install-extension cdt.context'
3481        report("running command: %s",command)
3482        os.execute(command)
3483    end
3484end
3485
3486if environment.arguments.generate then
3487    scripts.vscode.generate()
3488elseif environment.arguments.lsfile then
3489    scripts.vscode.ls()
3490elseif environment.arguments.start then
3491    scripts.vscode.start()
3492elseif environment.arguments.exporthelp then
3493    application.export(environment.arguments.exporthelp,environment.files[1])
3494else
3495    application.help()
3496end
3497
3498-- scripts.vscode.ls()
3499
3500-- scripts.vscode.generate([[t:/vscode/data/context/extensions]])
3501