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