textadept-context-runner.lua /size: 43 Kb    last modification: 2020-07-01 14:35
1local info = {
2    version   = 1.002,
3    comment   = "prototype textadept runner for context/metafun",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files",
7}
8
9-- This is an adapted version of the run code by mitchell.att.foicica.corunner. The main
10-- reason I started patching is that long lines got broken in the middle so we needed
11-- to have a more clever line splitter that saves half of a line for later. Then I
12-- decided to come up with a few more variants so in the end ... it's just too tempting
13-- make something that exactly suits out needs. In fact, maybe I'll do that some day:
14-- take core textadept and make a dedicated variant for the kind of processing that we
15-- do and make it suitable for document authors (the manual says that is doable). In that
16-- case I can also use a lot of already written helpers.
17--
18-- The error scanner is not needed. If I need one, it will be using a lexers applied
19-- afterwards because working on half lines is not going to work out well anyway.
20--
21-- Here I removed iconv calls as in context we use utf (less hassle with fonts too). One
22-- can always use the original approach.
23--
24-- The events seems to have hard coded names, Also, the name of the message buffer cannot
25-- be changes because otherwise we get a message when the session is restored. I don't
26-- care about locales.
27--
28-- Somehow the process hangs when I refresh the pdf viewer, this doesn't happen in scite so
29-- the underlying code is for the moment less reliant.
30
31local match, gsub, find, format, gmatch, rep = string.match, string.gsub, string.find, string.format, string.gmatch, string.rep
32local char, lower, upper, sub = string.char, string.lower, string.upper, string.sub
33local concat, sort = table.concat, table.sort
34local assert, type = assert, type
35
36local original            = textadept.run
37local runner              = { }
38
39runner.MARK_WARNING       = original.MARK_WARNING
40runner.MARK_ERROR         = original.MARK_ERROR
41
42local specifications      = { }
43runner.specifications     = specifications
44
45----- RUNNER_EVENT        = "[Context Runner]"
46local OUTPUT_BUFFER       = '[Message Buffer]' -- CONSOLE
47
48----- events.RUNNER_EVENT = RUNNER_EVENT
49
50local currentprocess      = nil
51local xbuffer             = nil
52
53local function find_buffer(buffer_type)
54    for i=1,#_BUFFERS do
55        local buffer = _BUFFERS[i]
56        if buffer._type == buffer_type then
57            return buffer
58        end
59    end
60end
61
62local function print_output(str)
63    local print_buffer = find_buffer(OUTPUT_BUFFER)
64    -- some simplified magic copied from the adeptext runner
65    if not print_buffer then
66        if not ui.tabs then
67            view:split()
68        end
69        print_buffer = buffer.new()
70        print_buffer._type = OUTPUT_BUFFER
71        events.emit(events.FILE_OPENED)
72    else
73        for i=1,#_VIEWS do
74            local view = _VIEWS[i]
75            if view.buffer._type == OUTPUT_BUFFER then
76                ui.goto_view(view)
77                break
78            end
79        end
80        if view.buffer._type ~= OUTPUT_BUFFER then
81            view:goto_buffer(print_buffer)
82        end
83    end
84    print_buffer:append_text(str)
85    print_buffer:goto_pos(buffer.length)
86    print_buffer:set_save_point()
87    return true -- quits
88end
89
90local function trace_output(str)
91    xbuffer = buffer
92    print_output(str)
93    if xbuffer then 
94        view:goto_buffer(xbuffer)
95    end 
96end 
97
98local function clear_output()
99    xbuffer = buffer
100    local print_buffer = find_buffer(OUTPUT_BUFFER)
101    if print_buffer then
102        print_buffer:clear_all()
103    end
104end
105
106local function is_output(buffer)
107    return buffer._type == OUTPUT_BUFFER
108end
109
110-- Instead of events we will have out own interceptors so that we don't have
111-- interference. The main problem is that we don't have much control over the
112-- order. If we have much actions I can always come up with something. 
113
114-- The textadept console seems a bit slower than the one in scite (which does some 
115-- output pane parsing so it could be even faster). Maybe it relates to the way 
116-- the program is run. Scite provides some more control over this. It might have
117-- to do with the way tex pipes to the console, because from a simple lua run it's 
118-- quite fast. Maybe calling cmd is not optimal. Anyhow, it means that for now I 
119-- should not use textadept when running performance test that need to compare with 
120-- the past. 
121
122local function process(buffer,filename,action)
123    if not filename then
124        filename = buffer.filename
125    end
126    if not filename then
127        return
128    end
129    if filename == buffer.filename then
130        buffer:annotation_clear_all() -- needed ?
131        io.save_file()
132    end
133    if filename == "" then
134        return
135    end
136    local suffix        = match(filename,'[^/\\.]+$')
137    local specification = specifications[suffix]
138    if not specification then
139        return
140    end
141    local action = specification[action]
142    local quitter = nil
143    if type(action) == "table" then
144        action  = action.command
145        quitter = action.quitter
146    end
147    if type(action) ~= "string" then
148        return
149    end
150    clear_output()
151    local pathpart = ''
152    local basename = filename
153    if find(filename,'[/\\]') then
154        pathpart, basename = match(filename,'^(.+[/\\])([^/\\]+)$')
155    end
156    -- beter strip one from the end
157    local nameonly = match(basename,'^(.+)%.')
158    -- more in sync which what we normally do (i'd rather use the ctx template mechanism)
159    local command = gsub(action,'%%(.-)%%', {
160        filename  = filename,
161        pathname  = dirname,
162        dirname   = dirname,
163        pathpart  = dirname,
164        basename  = basename,
165        nameonly  = nameonly,
166        suffix    = suffix,
167        selection = function() return match(buffer.get_sel_text(),"%s*([A-Za-z]+)") end,
168    })
169    -- for fun i'll add a ansi escape sequence lexer some day
170    local function emit_output(output)
171        print_output(output) -- events.emit(RUNNER_EVENT,...)
172        -- afaik there is no way to check if we're waiting for input (no input callback)
173       if quitter then
174           local quit, message = quitter(interceptor)
175           if quit then
176               if message then
177                   print_output(format("\n\n> quit: %s\n",message))
178               end
179               runner.quit()
180           end
181       end
182    end
183    local function exit_output(status)
184        print_output(format("\n\n> exit: %s, press esc to return to source\n",status)) -- events.emit(RUNNER_EVENT,...)
185    end
186    print_output(format("> command: %s\n",command)) -- events.emit(RUNNER_EVENT,...)
187    currentprocess = assert(os.spawn(command, pathpart, emit_output, emit_output, exit_output))
188end
189
190function runner.install(name)
191    return function(filename)
192        process(buffer,filename,name)
193    end
194end
195
196runner.check   = runner.install("check")
197runner.process = runner.install("process")
198runner.preview = runner.install("preview")
199
200function runner.resultof(command) -- from l-os.lua
201    local handle = io.popen(command,"r")
202    if handle then
203        local result = handle:read("*all") or ""
204        handle:close()
205        return result
206    else
207        return ""
208    end
209end
210
211function runner.quit()
212    if currentprocess then
213        assert(currentprocess:kill())
214    end
215end
216
217local function char_added(code)
218    if code == 10 and currentprocess and currentprocess:status() == 'running' and buffer._type == OUTPUT_BUFFER then
219        local line_num = buffer:line_from_position(buffer.current_pos) - 1
220        currentprocess:write((buffer:get_line(line_num)))
221    end
222    return true -- quits
223end
224
225function runner.goto_error(line, next)
226    -- see original code for how to do it
227end
228
229local function key_press(code)
230    if xbuffer and keys.KEYSYMS[code] == 'esc' then
231        view:goto_buffer(xbuffer)
232        return true
233    end
234end
235
236local function double_click()
237    if xbuffer and is_output(buffer) then
238        view:goto_buffer(xbuffer)
239        return true
240    end
241end
242
243-- 
244
245local l2 = char(0xC0)
246local l3 = char(0xE0)
247local l4 = char(0xF0)
248
249local function utflen(str)
250    local n = 0
251    local l = 0
252    for s in gmatch(str,".") do
253        if l > 0 then
254            l = l - 1
255        else
256            n = n + 1
257            if s >= l4 then
258                l = 3
259            elseif s >= l3 then
260                l = 2
261            elseif s >= l2 then
262                l = 1
263            end
264        end
265    end
266    return n
267end
268
269local function prepare()
270    local startposition = buffer.selection_start 
271    local endposition   = buffer.selection_end   
272
273    if startposition == endposition then return end
274
275    buffer.current_pos = startposition
276    buffer:home()
277
278    buffer.current_pos = endposition
279    buffer:line_end_extend()
280
281    local firstposition = buffer.selection_start 
282    local lastposition  = buffer.selection_end   
283
284    local firstline     = buffer:line_from_position(startposition)
285    local lastline      = buffer:line_from_position(endposition)
286
287    local startcolumn   = startposition - firstposition
288    local endcolumn     = lastposition  - endposition   + 1 
289    local selection     = buffer:get_sel_text()
290
291 -- trace_output(firstposition .. " " .. startposition .. "\n")
292 -- trace_output(endposition   .. " " .. lastposition  .. "\n")
293
294    return startposition, endposition, firstposition, lastposition, startcolumn, endcolumn, firstline, lastline, selection
295end 
296
297local function replace(startposition,lastposition,replacement)
298    if type(replacement) == "table" then 
299        replacement = concat(replacement,"\n")
300    end 
301 -- trace_output(replacement .. "\n")
302
303    buffer.current_pos = startposition
304
305    buffer:begin_undo_action()
306    buffer:set_target_range(startposition,lastposition)
307    buffer:replace_target(replacement)
308    buffer:end_undo_action()
309
310    buffer.selection_start = startposition
311    buffer.selection_end   = startposition
312end
313
314-- This is old code, from my early lua days, so not that nice and optimal, but 
315-- no one sees it and performance is irrelevant here. 
316
317local magicstring = rep("<ctx-crlf/>", 2)
318
319function runner.wrap()
320
321    local startposition, endposition, firstposition, lastposition, startcolumn, endcolumn, firstline, lastline, selection = prepare()
322
323    if not startposition then 
324        return 
325    end 
326
327    local wraplength  = buffer.wrap_length
328    local length      = tonumber(wraplength) or 80
329    local replacement = { }
330    local templine    = ""
331    local tempsize    = 0
332    local indentation = rep(' ',startcolumn)
333
334    selection = gsub(selection,"[\n\r][\n\r]","\n")
335    selection = gsub(selection,"\n\n+"," " .. magicstring .. " ")
336    selection = gsub(selection,"^%s",'')
337
338    for snippet in gmatch(selection,"%S+") do
339        if snippet == magicstring then
340            replacement[#replacement+1] = templine
341            replacement[#replacement+1] = ""
342            templine = ""
343            tempsize = 0
344        else
345            local snipsize = utflen(snippet)
346            if tempsize + snipsize > length then
347                replacement[#replacement+1] = templine
348                templine = indentation .. snippet
349                tempsize = startcolumn + snipsize
350            elseif tempsize == 0 then
351                templine = indentation .. snippet
352                tempsize = tempsize + startcolumn + snipsize
353            else
354                templine = templine .. " " .. snippet
355                tempsize = tempsize + 1 + snipsize
356            end
357        end
358    end
359    
360    replacement[#replacement+1] = templine  
361    replacement[1] = gsub(replacement[1],"^%s+","")
362
363    if endcolumn == 0 then
364        replacement[#replacement+1] = ""
365    end
366
367    replace(startposition,lastposition,replacement)
368
369end 
370
371local magicstring = rep("<multiplelines/>", 2)
372
373function runner.unwrap()
374
375    local startposition, endposition, firstposition, lastposition, startcolumn, endcolumn, selection, firstline, lastline = prepare()
376
377    if not startposition then 
378        return 
379    end 
380
381    startposition = firstposition 
382    endposition   = lastposition 
383
384    local selection   = gsub(selection,"[\n\r][\n\r]+", " " .. magicstring .. " ")
385    local replacement = { } 
386
387    for snippet in gmatch(selection,"%S+") do
388        replacement[#replacement+1] = snippet == magicstring and "" or snippet
389    end
390
391    if endcolumn == 0 then
392        replacement[#replacement+1] = ""
393    end
394
395    replace(startposition,lastposition,replacement)
396
397end 
398
399-- This is real old crappy code which doesn't really pass my current qa standards but 
400-- it does the job so ... (hashing the blobs would work ok). 
401
402local function grab(str,delimiter)
403    local list = { }
404    for snippet in gmatch(str,delimiter) do
405        list[#list+1] = snippet
406    end
407    return list
408end
409
410local function alphacmp_yes(a,b)
411    return lower(gsub(sub(a,i),"0"," ")) < lower(gsub(sub(b,i),"0"," "))
412end
413
414local function alphacmp_nop(a,b)
415    return lower(a) < lower(b)
416end
417
418local function alphasort(list,i)    
419    sort(list,i and i > 0 and alphacmp_yes or alphacmp_nop)
420end
421
422function runner.sort()
423
424    local startposition, endposition, firstposition, lastposition, startcolumn, endcolumn, firstline, lastline, selection = prepare()
425
426    if not startposition then 
427        return 
428    end 
429
430    startposition = firstposition 
431    endposition   = lastposition 
432
433    local list = grab(selection,"[^\n\r]+")
434
435    alphasort(list,startcolumn)
436
437    if endcolumn == 0 then
438        list[#list+1] = ""
439    end
440
441    replace(startposition,lastposition,list)
442
443end 
444
445-- Tricky: we can't reset an event (because we need to know the function which is
446-- local. So, a first solution injected a false into the table which will trigger
447-- a break and then I found out that returning true has the same effect. Then I
448-- found out that we can have our own events and next decided not to use them at
449-- all.
450
451-- events.connect(events.RUNNER_EVENT,   print_output, 1)
452
453events.connect(events.CHAR_ADDED,     char_added,   1)
454events.connect(events.KEYPRESS,       key_press,    1)
455events.connect(events.DOUBLE_CLICK,   double_click, 1)
456
457-- We need to get rid of the crash due to macros.lua event crash in 
458-- 
459-- -- textadept.menu.menubar[_L['_Tools']][_L['Select Co_mmand']][2],
460
461-- for i=1,#_VIEWS do
462--     if _VIEWS[i].buffer._type == "[Message Buffer]" then 
463--         ui.goto_view(_VIEWS[i])
464--         buffer.current_pos = buffer.current_pos
465--         io.close_buffer()
466--         break 
467--     end
468-- end
469-- for i = 1, #_BUFFERS do
470--     if _BUFFERS[i]._type == "[Message Buffer]" then 
471--         view:goto_buffer(_BUFFERS[i]) 
472--         buffer.current_pos = buffer.current_pos
473--         io.close_buffer()
474--         break 
475--     end
476-- end
477
478-- I don't want the indentation. I also want an extra space which in turn means 
479-- a more extensive test. I also don't care about a suffix. Adapted a bit to 
480-- match the code above. 
481
482function runner.blockcomment()
483    local buffer  = buffer
484    local comment = textadept.editing.comment_string[buffer:get_lexer(true)]
485
486    if not comment or comment == "" then 
487        return 
488    end
489
490    local prefix     = comment:match('^([^|]+)|?([^|]*)$')
491    local usedprefix = prefix 
492
493    if not prefix then 
494        return     
495    end
496
497    if not find(prefix,"%s$") then 
498        usedprefix = prefix .. " "
499    end
500
501    local n_prefix      = #prefix
502    local n_usedprefix  = #usedprefix
503
504    local startposition = buffer.selection_start
505    local endposition   = buffer.selection_end
506    local firstline     = buffer:line_from_position(startposition)
507    local lastline      = buffer:line_from_position(endposition)
508
509    if firstline ~= lastline and endposition == buffer:position_from_line(lastline) then 
510        lastline = lastline - 1 
511    end 
512
513    startposition = buffer.line_end_position[startposition] - startposition
514    endposition   = buffer.length - endposition
515
516    buffer:begin_undo_action()
517
518    for line=firstline,lastline do
519        local p = buffer:position_from_line(line)
520        if buffer:text_range(p, p + n_usedprefix) == usedprefix then
521            buffer:delete_range(p, n_usedprefix)
522        elseif buffer:text_range(p, p + n_prefix) == prefix then
523            buffer:delete_range(p, n_prefix)
524        else
525            buffer:insert_text(p, usedprefix)
526        end
527    end
528
529    buffer:end_undo_action()
530
531    startposition = buffer.line_end_position[firstline] - startposition
532    endposition   = buffer.length - endposition
533
534    -- whatever ... 
535
536    local start_pos = buffer:position_from_line(firstline)
537
538    if start_pos > startposition then 
539        startposition = start_pos 
540    end 
541    if start_pos > endposition then 
542        endposition = start_pos 
543    end 
544
545    if firstline ~= lastline then 
546        buffer:set_sel(startposition, endposition) 
547    else 
548        buffer:goto_pos(endposition) 
549    end
550end
551
552-- This only works partially as for some reason scite shows proper math symbols while 
553-- here we don't see them. I need to look into that. 
554
555local textlists = { -- taken from sort-lan.lua
556    en = {
557        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
558        "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
559        "u", "v", "w", "x", "y", "z",
560
561        "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
562        "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
563        "U", "V", "W", "X", "Y", "Z",
564    },
565    nl = {
566        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
567        "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
568        "u", "v", "w", "x", "y", "z",
569
570        "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
571        "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
572        "U", "V", "W", "X", "Y", "Z",
573    },
574    fr = {
575        "a", "ĂŠ", "b", "c", "ç", "d", "e", "Ăš", "Ă©", "ĂȘ",
576        "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
577        "p", "q", "r", "s", "t", "u", "v", "w", "x", "y",
578        "z",
579
580        "A", "Æ", "B", "C", "Ç", "D", "E", "È", "É", "Ê",
581        "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
582        "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
583        "Z",
584
585    },
586    de = {
587        "a", "Ă€", "b", "c", "d", "e", "f", "g", "h", "i",
588        "j", "k", "l", "m", "n", "o", "ö", "p", "q", "r",
589        "s", "ß", "t", "u", "ĂŒ", "v", "w", "x", "y", "z",
590
591        "A", "Ä", "B", "C", "D", "E", "F", "G", "H", "I",
592        "J", "K", "L", "M", "N", "O", "Ö", "P", "Q", "R",
593        "S", "SS", "T", "U", "Ü", "V", "W", "X", "Y", "Z",
594    },
595    fi = { -- finish
596        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
597        "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
598        "u", "v", "w", "x", "y", "z", "Ä", "À", "ö",
599
600        "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
601        "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
602        "U", "V", "W", "X", "Y", "Z", "Å", "Ä", "Ö",
603    },
604    sl = { -- slovenian
605        "a", "b", "c", "č", "ć", "d", "đ", "e", "f", "g", "h", "i",
606        "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "ĆĄ", "t",
607        "u", "v", "w", "x", "y", "z", "ĆŸ",
608
609        "A", "B", "C", "Č", "Ć", "D", "Đ", "E", "F", "G", "H", "I",
610        "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "Ć ", "T",
611        "U", "V", "W", "X", "Y", "Z", "Ćœ",
612    },
613    ru = { -- rusian
614        "Đ°", "б", "ĐČ", "Đł", "ĐŽ", "Đ”", "ё", "ж", "Đ·", "Đž",
615        "і", "Đč", "Đș", "Đ»", "ĐŒ", "Đœ", "ĐŸ", "Đż", "р", "с",
616        "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ы",
617        "ь", "ŃŁ", "э", "ю", "я", "Ńł", "Ń”",
618
619        "А", "Б", "В", "Г", "Д", "Е", "Ё", "Ж", "З", "И",
620        "І", "Й", "К", "Л", "М", "Н", "О", "П", "Р", "С",
621        "Đą", "ĐŁ", "Đ€", "Đ„", "ĐŠ", "Ч", "Đš", "Đ©", "ĐȘ", "Đ«",
622        "ĐŹ", "Ńą", "Đ­", "Đź", "ĐŻ", "ŃČ", "ŃŽ",
623    },
624    uk = { -- ukraninuan
625        "Đ°", "б", "ĐČ", "Đł", "ґ", "ĐŽ", "Đ”", "є", "ж", "Đ·", "Đž", "і",
626        "ї", "Đč", "Đș", "Đ»", "ĐŒ", "Đœ", "ĐŸ", "Đż", "р", "с", "т", "у",
627        "ф", "х", "ц", "ч", "ш", "щ", "ь", "ю", "я",
628
629        "А", "Б", "В", "Г", "Ґ", "Д", "Е", "Є", "Ж", "З", "И", "І",
630        "Ї", "Й", "К", "Л", "М", "Н", "О", "П", "Р", "С", "б", "У",
631        "Ѐ", "Є", "Њ", "Ч", "К", "Щ", "Џ", "П", "Я",
632    },
633    be = { -- belarusia
634        "Đ°", "б", "ĐČ", "Đł", "ĐŽ", "Đ”", "ё", "ж", "Đ·", "і",
635        "Đč", "Đș", "Đ»", "ĐŒ", "Đœ", "ĐŸ", "Đż", "р", "с", "т",
636        "у", "ў", "ф", "х", "ц", "ч", "ш", "ы", "ь", "э",
637        "ю", "я",
638
639        "А", "Б", "В", "Г", "Д", "Е", "Ё", "Ж", "З", "І",
640        "Й", "К", "Л", "М", "Н", "О", "П", "Р", "С", "б",
641        "ĐŁ", "Ў", "Đ€", "Đ„", "ĐŠ", "Ч", "Đš", "Đ«", "ĐŹ", "Đ­",
642        "Đź", "ĐŻ",
643    },
644    bg = { -- bulgarian
645        "Đ°", "б", "ĐČ", "Đł", "ĐŽ", "Đ”", "ж", "Đ·","Đž", "Đč",
646        "Đș", "a", "Đ»", "a", "ĐŒ", "Đœ", "ĐŸ", "Đż", "р", "с",
647        "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ь",
648        "ю", "я",
649
650        "А", "Б", "В", "Г", "Д", "Е", "Ж", "З","И", "Й",
651        "К", "A", "Л", "A", "М", "Н", "О", "П", "Р", "С",
652        "Đą", "ĐŁ", "Đ€", "Đ„", "ĐŠ", "Ч", "Đš", "Đ©", "ĐȘ", "ĐŹ",
653        "Đź", "ĐŻ",
654    },
655    pl = { -- polish
656        "a", "ą", "b", "c", "ć", "d", "e", "ę", "f", "g",
657        "h", "i", "j", "k", "l", "Ƃ", "m", "n", "Ƅ", "o",
658        "ó", "p", "q", "r", "s", "ƛ", "t", "u", "v", "w",
659        "x", "y", "z", "Ćș", "ĆŒ",
660
661        "A", "Ą", "B", "C", "Ć", "D", "E", "Ę", "F", "G",
662        "H", "I", "J", "K", "L", "Ɓ", "M", "N", "ƃ", "O",
663        "Ó", "P", "Q", "R", "S", "ƚ", "T", "U", "V", "W",
664        "X", "Y", "Z", "Ćč", "Ć»",
665    },
666    cz = { -- czech
667        "a", "ĂĄ", "b", "c", "č", "d", "ď", "e", "Ă©", "ě",
668        "f", "g", "h", "i", "Ă­", "j", "k", "l", "m",
669        "n", "ƈ", "o", "ó", "p", "q", "r", "ƙ", "s", "ơ",
670        "t", "Ć„", "u", "Ăș",  "ĆŻ", "v", "w", "x",  "y", "Ăœ",
671        "z", "ĆŸ",
672
673        "A", "Á", "B", "C", "Č", "D", "Ď", "E", "É", "Ě",
674        "F", "G", "H", "I", "Í", "J", "K", "L", "M",
675        "N", "Ƈ", "O", "Ó", "P", "Q", "R", "Ƙ", "S", "Ơ",
676        "T", "Ć€", "U", "Ú",  "Ćź", "V", "W", "X",  "Y", "Ý",
677        "Z", "Ćœ",
678    },
679    sk = { -- slovak
680        "a", "ĂĄ", "Ă€", "b", "c", "č", "d", "ď",
681        "e", "Ă©", "f", "g", "h", ch,  "i", "Ă­", "j", "k",
682        "l", "Äș", "ÄŸ", "m", "n", "ƈ", "o", "Ăł", "ĂŽ", "p",
683        "q", "r", "ƕ", "s", "ĆĄ", "t", "Ć„", "u", "Ăș", "v",
684        "w", "x", "y", "Ăœ", "z", "ĆŸ",
685
686        "A", "Á", "Ä", "B", "C", "Č", "D", "Ď",
687        "E", "É", "F", "G", "H", "I", "Í", "J", "K",
688        "L", "Äč", "Äœ", "M", "N", "Ƈ", "O", "Ó", "Ô", "P",
689        "Q", "R", "Ɣ", "S", "Ć ", "T", "Ć€", "U", "Ú", "V",
690        "W", "X", "Y", "Ý", "Z", "Ćœ",
691    },
692    hr = { -- croatian
693        "a", "b", "c", "č", "ć", "d", "đ", "e", "f",
694        "g", "h", "i", "j", "k", "l", "m", "n",
695        "o", "p", "r", "s", "ĆĄ", "t", "u", "v", "z", "ĆŸ",
696
697        "A", "B", "C", "Č", "Ć", "D", "Đ", "E", "F",
698        "G", "H", "I", "J", "K", "L", "M", "N",
699        "O", "P", "R", "S", "Ć ", "T", "U", "V", "Z", "Ćœ",
700    },
701    sr = { -- serbian
702        "Đ°", "б", "ĐČ", "Đł", "ĐŽ", "ђ", "Đ”", "ж", "Đ·", "Đž",
703        "ј", "Đș", "Đ»", "љ", "ĐŒ", "Đœ", "њ", "ĐŸ", "Đż", "р",
704        "с", "т", "ћ", "у", "ф", "х", "ц", "ч", "џ", "ш",
705
706        "А", "Б", "В", "Г", "Д", "Ђ", "Е", "Ж", "З", "И",
707        "Ј", "К", "Л", "Љ", "М", "Н", "Њ", "О", "П", "Р",
708        "ĐĄ", "Đą", "Ћ", "ĐŁ", "Đ€", "Đ„", "ĐŠ", "Ч", "Џ", "Đš",
709    },
710    no = { -- norwegian
711        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
712        "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
713        "u", "v", "w", "x", "y", "z", "ĂŠ", "Ăž", "Ă„",
714
715        "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
716        "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
717        "U", "V", "W", "X", "Y", "Z", "Æ", "Ø", "Å",
718    },
719    da = { --danish
720        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
721        "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
722        "u", "v", "w", "x", "y", "z", "ĂŠ", "Ăž", "Ă„",
723
724        "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
725        "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
726        "U", "V", "W", "X", "Y", "Z", "Æ", "Ø", "Å",
727    },
728    sv = { -- swedish
729        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
730        "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
731        "u", "v", "w", "x", "y", "z", "Ä", "À", "ö",
732
733        "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
734        "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
735        "U", "V", "W", "X", "Y", "Z", "Å", "Ä", "Ö",
736    },
737    is = { -- islandic
738        "a", "ĂĄ", "b", "d", "Ă°", "e", "Ă©", "f", "g", "h",
739        "i", "Ă­", "j", "k", "l", "m", "n", "o", "Ăł", "p",
740        "r", "s", "t", "u", "Ăș", "v", "x", "y", "Ăœ", "ĂŸ",
741        "Ê", "ö",
742
743        "A", "Á", "B", "D", "Ð", "E", "É", "F", "G", "H",
744        "I", "Í", "J", "K", "L", "M", "N", "O", "Ó", "P",
745        "R", "S", "T", "U", "Ú", "V", "X", "Y", "Ý", "Þ",
746        "Æ", "Ö",
747    },
748 -- gr = { -- greek
749 --     "α", "ÎŹ", "ᜰ", "៶", "áŸł", "ጀ", "ጁ", "ጄ", "ጂ", "ጆ",
750 --     "ጁ", "ጅ", "ጃ", "ጇ", "េ", "៎", "áŸČ", "៷", "ោ", "ែ",
751 --     "ៅ", "ៃ", "ំ", "ះ", "ÎČ", "Îł", "ÎŽ", "Δ", "έ", "áœČ",
752 --     "ጐ", "ጔ", "ጒ", "጑", "ጕ", "ጓ", "ζ", "η", "η", "Îź",
753 --     "ᜎ", "ῆ", "ῃ", "ጠ", "ጀ", "áŒą", "ጊ", "័", "áŒĄ", "ጄ",
754 --     "áŒŁ", "ጧ", "៑", "ῄ", "ῂ", "ῇ", "។", "្", "៕", "៓",
755 --     "៖", "ៗ", "Ξ", "Îč", "ÎŻ", "᜶", "ῖ", "ጰ", "ጎ", "áŒČ",
756 --     "ጶ", "ጱ", "ጔ", "áŒł", "ጷ", "ϊ", "ΐ", "ῒ", "ῗ", "Îș",
757 --     "λ", "ÎŒ", "Îœ", "Ο", "Îż", "ό", "᜞", "ᜀ", "ᜄ", "ᜂ",
758 --     "ᜁ", "ᜅ", "ᜃ", "π", "ρ", "῀", "áż„", "σ", "ς", "τ",
759 --     "υ", "ύ", "áœș", "Ὴ", "ᜐ", "᜔", "ᜒ", "᜖", "ᜑ", "᜕",
760 --     "ᜓ", "᜗", "ϋ", "ΰ", "áżą", "ῧ", "φ", "χ", "ψ", "ω",
761 --     "ώ", "ᜌ", "ῶ", "áżł", "ᜠ", "ᜀ", "áœą", "ᜊ", "០", "áœĄ",
762 --     "ᜄ", "áœŁ", "ᜧ", "áŸĄ", "῎", "áżČ", "áż·", "ៀ", "áŸą", "ោ",
763 --     "áŸŁ", "៊", "៧",
764 --
765 --     "Α", "Ά", "áŸș", "Α͂", "ገ", "ጉ", "ጌ", "ጊ", "ጎ",
766 --     "ጉ", "ግ", "ጋ", "ጏ",
767 --     "Β", "Γ", "Δ", "Ε", "Έ", "Ὲ",
768 --     "ጘ", "ጜ", "ጚ", "ጙ", "ጝ", "ጛ", "Ζ", "Η", "Η", "Ή",
769 --     "Ὴ", "Η͂", "ጚ", "áŒŹ", "áŒȘ", "áŒź", "ጩ", "ጭ",
770 --     "ጫ", "áŒŻ",
771 --     "Θ", "Ι", "Ί", "Ὶ", "Ι͂", "ጞ", "ጌ", "áŒș",
772 --     "ጟ", "áŒč", "ጜ", "ጻ", "áŒż", "ÎȘ", "Ϊ́", "Ϊ̀", "Ϊ͂", "Κ",
773 --     "Λ", "Μ", "Ν", "Ξ", "Ο", "Ό", "áżž", "ᜈ", "ᜌ", "ᜊ",
774 --     "ᜉ", "ᜍ", "ᜋ", "Π", "ÎĄ", "ÎĄÌ“", "῏", "ÎŁ", "ÎŁ", "΀",
775 --     "΄", "Ύ", "áżȘ", "΄͂", "΄̓", "΄̓́", "΄̓̀", "΄̓͂", "᜙", "᜝",
776 --     "᜛", "ᜟ", "Ϋ", "΄̈́", "΄̈̀", "΄̈͂", "Ί", "Χ", "Κ", "Ω",
777 --     "Ώ", "áżș", "Ω͂", "᜚", "áœŹ", "áœȘ", "áœź", "ᜩ",
778 --     "ᜭ", "ᜫ", "áœŻ",
779 --     },
780    gr = { -- greek
781        "α", "ÎČ", "Îł", "ÎŽ", "Δ", "ζ", "η", "Ξ", "Îč", "Îș",
782        "λ", "ÎŒ", "Îœ", "Ο", "Îż", "π", "ρ", "ς", "τ", "υ",
783        "φ", "χ", "ψ", "ω",
784
785        "Α", "Β", "Γ", "Δ", "Ε", "Ζ", "Η", "Θ", "Ι", "Κ",
786        "Λ", "Μ", "Ν", "Ξ", "Ο", "Π", "ÎĄ", "ÎŁ", "΀", "΄",
787        "Χ", "Κ", "Ω",
788        },
789    la = { -- latin
790        "a", "ā", "ă", "b", "c", "d", "e", "ē", "ĕ", "f",
791        "g", "h", "i", "Ä«", "Ä­", "j", "k", "l", "m", "n",
792        "o", "ƍ", "Ə", "p", "q", "r", "s", "t", "u", "Ć«",
793        "Ć­", "v", "w", "x", "y", "Èł", "y̆", "z", "ĂŠ",
794
795        "A", "Ā", "Ă", "B", "C", "D", "E", "Ē", "Ĕ", "F",
796        "G", "H", "I", "ÄȘ", "ÄŹ", "J", "K", "L", "M", "N",
797        "O", "ƌ", "Ǝ", "P", "Q", "R", "S", "T", "U", "ĆȘ",
798        "ĆŹ", "V", "W", "X", "Y", "ÈČ", "Y̆", "Z", "Æ",
799    },
800    it = { -- italian
801        "a", "ĂĄ", "b", "c", "d", "e", "Ă©", "Ăš", "f", "g",
802        "h", "i", "Ă­", "ĂŹ", "j", "k", "l", "m", "n", "o",
803        "Ăł", "ĂČ", "p", "q", "r", "s", "t", "u", "Ăș", "Ăč",
804        "v", "w", "x", "y", "z",
805
806        "A", "Á", "B", "C", "D", "E", "É", "È", "F", "G",
807        "H", "I", "Í", "Ì", "J", "K", "L", "M", "N", "O",
808        "Ó", "Ò", "P", "Q", "R", "S", "T", "U", "Ú", "Ù",
809        "V", "W", "X", "Y", "Z",
810    },
811    ro = { -- romanian
812        "a", "ă", "ù", "b", "c", "d", "e", "f", "g", "h",
813        "i", "Ăź", "j", "k", "l", "m", "n", "o", "p", "q",
814        "r", "s", "ș", "t", "ț", "u", "v", "w", "x", "y",
815        "z",
816
817        "A", "Ă", "Â", "B", "C", "D", "E", "F", "G", "H",
818        "I", "Î", "J", "K", "L", "M", "N", "O", "P", "Q",
819        "R", "S", "Ș", "T", "Ț", "U", "V", "W", "X", "Y",
820        "Z",
821    },
822    es = { -- spanish
823        "a", "ĂĄ", "b", "c", "d", "e", "Ă©", "f", "g", "h",
824        "i", "í", "j", "k", "l", "m", "n", "ñ", "o", "ó",
825        "p", "q", "r", "s", "t", "u", "Ăș", "ĂŒ", "v", "w",
826        "x", "y", "z",
827
828        "A", "Á", "B", "C", "D", "E", "É", "F", "G", "H",
829        "I", "Í", "J", "K", "L", "M", "N", "Ñ", "O", "Ó",
830        "P", "Q", "R", "S", "T", "U", "Ú", "Ü", "V", "W",
831        "X", "Y", "Z",
832    },
833    pt = { -- portuguese
834        "a", "å", "ù", "ã", "à", "b", "c", "ç", "d", "e",
835        "Ă©", "ĂȘ", "f", "g", "h", "i", "Ă­", "j", "k", "l",
836        "m", "n", "o", "Ăł", "ĂŽ", "Ă”", "p", "q", "r", "s",
837        "t", "u", "Ăș", "ĂŒ", "v", "w", "x", "y", "z",
838
839        "A", "Á", "Â", "Ã", "À", "B", "C", "Ç", "D", "E",
840        "É", "Ê", "F", "G", "H", "I", "Í", "J", "K", "L",
841        "M", "N", "O", "Ó", "Ô", "Õ", "P", "Q", "R", "S",
842        "T", "U", "Ú", "Ü", "V", "W", "X", "Y", "Z",
843    },
844    lt = { -- lithuanian
845        "a", "ą", "b", "c", "ch",  "č", "d", "e", "ę", "ė",
846        "f", "g", "h", "i", "ÄŻ", "y", "j", "k", "l", "m",
847        "n", "o", "p", "r", "s", "ĆĄ", "t", "u", "Ćł", "Ć«",
848        "v", "z", "ĆŸ",
849
850        "A", "Ą", "B", "C", "CH",  "Č", "D", "E", "Ę", "Ė",
851        "F", "G", "H", "I", "Äź", "Y", "J", "K", "L", "M",
852        "N", "O", "P", "R", "S", "Ć ", "T", "U", "ĆČ", "ĆȘ",
853        "V", "Z", "Ćœ",
854    },
855    lv = { -- latvian
856        "a", "ā", "b", "c", "č", "d", "e", "ē", "f", "g",
857        "ÄŁ", "h", "i", "Ä«", "j", "k", "Ä·", "l", "ÄŒ", "m",
858        "n", "Ɔ", "o", "ƍ", "p", "r", "Ɨ", "s", "ơ", "t",
859        "u", "Ć«", "v", "z", "ĆŸ",
860
861        "A", "Ā", "B", "C", "Č", "D", "E", "Ē", "F", "G",
862        "Äą", "H", "I", "ÄȘ", "J", "K", "Ķ", "L", "Ä»", "M",
863        "N", "ƅ", "O", "ƌ", "P", "R", "Ɩ", "S", "Ơ", "T",
864        "U", "ĆȘ", "V", "Z", "Ćœ",
865    },
866    hu = { -- hungarian
867        "a", "ĂĄ", "b", "c", "d", "e", "Ă©",
868        "f", "g", "h", "i", "Ă­", "j", "k", "l",
869        "m", "n", "o", "Ăł", "ö", "Ƒ", "p", "q", "r",
870        "s",  "t", "u", "Ăș", "ĂŒ", "Ʊ", "v", "w",
871        "x", "y", "z",
872
873        "A", "Á", "B", "C", "D", "E", "É",
874        "F", "G", "H", "I", "Í", "J", "K", "L",
875        "M", "N", "O", "Ó", "Ö", "Ɛ", "P", "Q", "R",
876        "S",  "T", "U", "Ú", "Ü", "ư", "V", "W",
877        "X", "Y", "Z",
878    },
879    et = { -- estonian
880        "a", "b", "d", "e", "f", "g", "h", "i", "j", "k",
881        "l", "m", "n", "o", "p", "r", "s", "ĆĄ", "z", "ĆŸ",
882        "t", "u", "v", "w", "Ă”", "Ă€", "ö", "ĂŒ", "x", "y",
883
884        "A", "B", "D", "E", "F", "G", "H", "I", "J", "K",
885        "L", "M", "N", "O", "P", "R", "S", "Ć ", "Z", "Ćœ",
886        "T", "U", "V", "W", "Õ", "Ä", "Ö", "Ü", "X", "Y",
887    },
888 -- jp = { -- japanese
889 --     "あ", "い", "う", "え", "お", "か", "き", "く", "け", "こ",
890 --     "さ", "し", "す", "せ", "そ", "た", "づ", "぀", "お", "ず",
891 --     "ăȘ", "に", "く", "ね", "た", "は", "ăČ", "ご", "ぞ", "ほ",
892 --     "ăŸ", "み", "む", "め", "も", "や", "ゆ", "よ",
893 --     "ら", "り", "る", "れ", "ろ", "わ", "ゐ", "ゑ", "を", "ん",
894 -- },
895}
896
897local textselector = { }
898for k, v in next, textlists do
899    textselector[#textselector+1] = k
900end
901sort(textselector)
902
903local mathsets = {
904    { "tf", {
905        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
906        "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
907        "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
908    }, },
909    { "bf", {
910        "𝐛", "𝐜", "𝐝", "𝐞", "𝐟", "𝐠", "𝐡", "𝐱", "𝐣", "đ€", "đ„", "𝐩", "𝐧", "𝐹", "đ©", "đȘ", "đ«", "𝐬", "𝐭", "𝐼", "𝐯", "𝐰", "đ±", "đČ", "𝐳",
911        "𝐀", "𝐁", "𝐂", "𝐃", "𝐄", "𝐅", "𝐆", "𝐇", "𝐈", "𝐉", "𝐊", "𝐋", "𝐌", "𝐍", "𝐎", "𝐏", "𝐐", "𝐑", "𝐒", "𝐓", "𝐔", "𝐕", "𝐖", "𝐗", "𝐘", "𝐙", "𝐚",
912        "𝟎", "𝟏", "𝟐", "𝟑", "𝟒", "𝟓", "𝟔", "𝟕", "𝟖", "𝟗"
913    }, },
914    { "it",           {
915        "𝑎", "𝑏", "𝑐", "𝑑", "𝑒", "𝑓", "𝑔", "ℎ", "𝑖", "𝑗", "𝑘", "𝑙", "𝑚", "𝑛", "𝑜", "𝑝", "𝑞", "𝑟", "𝑠", "𝑡", "𝑱", "𝑣", "đ‘€", "đ‘„", "𝑩", "𝑧",
916        "𝐮", "đ”", "đ¶", "đ·", "𝐾", "đč", "đș", "đ»", "đŒ", "đœ", "đŸ", "𝐿", "𝑀", "𝑁", "𝑂", "𝑃", "𝑄", "𝑅", "𝑆", "𝑇", "𝑈", "𝑉", "𝑊", "𝑋", "𝑌", "𝑍",
917    }, },
918    { "bi",           {
919        "𝒂", "𝒃", "𝒄", "𝒅", "𝒆", "𝒇", "𝒈", "𝒉", "𝒊", "𝒋", "𝒌", "𝒍", "𝒎", "𝒏", "𝒐", "𝒑", "𝒒", "𝒓", "𝒔", "𝒕", "𝒖", "𝒗", "𝒘", "𝒙", "𝒚", "𝒛",
920        "𝑹", "đ‘©", "đ‘Ș", "đ‘«", "𝑬", "𝑭", "𝑼", "𝑯", "𝑰", "đ‘±", "đ‘Č", "𝑳", "𝑮", "đ‘”", "đ‘¶", "đ‘·", "𝑾", "đ‘č", "đ‘ș", "đ‘»", "đ‘Œ", "đ‘œ", "đ‘Ÿ", "𝑿", "𝒀", "𝒁",
921    }, },
922    { "sc",       {
923        "đ’”", "đ’¶", "đ’·", "𝒾", "đ’č", "ℯ", "đ’»", "ℊ", "đ’œ", "đ’Ÿ", "𝒿", "𝓀", "𝓁", "𝓂", "𝓃", "℮", "𝓅", "𝓆", "𝓇", "𝓈", "𝓉", "𝓊", "𝓋", "𝓌", "𝓍", "𝓎", "𝓏",
924        "𝒜", "ℬ", "𝒞", "𝒟", "ℰ", "ℱ", "𝒱", "ℋ", "ℐ", "đ’„", "𝒩", "ℒ", "ℳ", "đ’©", "đ’Ș", "đ’«", "𝒬", "ℛ", "𝒼", "𝒯", "𝒰", "đ’±", "đ’Č", "𝒳", "𝒮",
925    }, },
926    { "sc bf",   {
927        "đ“Ș", "đ“«", "𝓬", "𝓭", "𝓼", "𝓯", "𝓰", "đ“±", "đ“Č", "𝓳", "𝓮", "đ“”", "đ“¶", "đ“·", "𝓾", "đ“č", "đ“ș", "đ“»", "đ“Œ", "đ“œ", "đ“Ÿ", "𝓿", "𝔀", "𝔁", "𝔂", "𝔃",
928        "𝓐", "𝓑", "𝓒", "𝓓", "𝓔", "𝓕", "𝓖", "𝓗", "𝓘", "𝓙", "𝓚", "𝓛", "𝓜", "𝓝", "𝓞", "𝓟", "𝓠", "𝓡", "𝓱", "𝓣", "đ“€", "đ“„", "𝓩", "𝓧", "𝓹", "đ“©",
929    }, },
930    { "fr",      {
931        "𝔞", "𝔟", "𝔠", "𝔡", "𝔱", "𝔣", "đ”€", "đ”„", "𝔩", "𝔧", "𝔹", "đ”©", "đ”Ș", "đ”«", "𝔬", "𝔭", "𝔼", "𝔯", "𝔰", "đ”±", "đ”Č", "𝔳", "𝔮", "đ””", "đ”¶", "đ”·",
932        "𝔄", "𝔅", "ℭ", "𝔇", "𝔈", "𝔉", "𝔊", "ℌ", "ℑ", "𝔍", "𝔎", "𝔏", "𝔐", "𝔑", "𝔒", "𝔓", "𝔔", "ℜ", "𝔖", "𝔗", "𝔘", "𝔙", "𝔚", "𝔛", "𝔜", "ℹ",
933    }, },
934    { "ds", {
935        "𝕓", "𝕔", "𝕕", "𝕖", "𝕗", "𝕘", "𝕙", "𝕚", "𝕛", "𝕜", "𝕝", "𝕞", "𝕟", "𝕠", "𝕡", "𝕱", "𝕣", "đ•€", "đ•„", "𝕩", "𝕧", "𝕹", "đ•©", "đ•Ș", "đ•«",
936        "𝔾", "đ”č", "ℂ", "đ”»", "đ”Œ", "đ”œ", "đ”Ÿ", "ℍ", "𝕀", "𝕁", "𝕂", "𝕃", "𝕄", "ℕ", "𝕆", "ℙ", "ℚ", "ℝ", "𝕊", "𝕋", "𝕌", "𝕍", "𝕎", "𝕏", "𝕐", "â„€", "𝕒",
937        "𝟘", "𝟙", "𝟚", "𝟛", "𝟜", "𝟝", "𝟞", "𝟟", "𝟠", "𝟡"
938    }, },
939    { "fr bf",  {
940        "𝕬", "𝕭", "𝕼", "𝕯", "𝕰", "đ•±", "đ•Č", "𝕳", "𝕮", "đ•”", "đ•¶", "đ•·", "𝕾", "đ•č", "đ•ș", "đ•»", "đ•Œ", "đ•œ", "đ•Ÿ", "𝕿", "𝖀", "𝖁", "𝖂", "𝖃",
941        "𝖄", "𝖅", "𝖆", "𝖇", "𝖈", "𝖉", "𝖊", "𝖋", "𝖌", "𝖍", "𝖎", "𝖏", "𝖐", "𝖑", "𝖒", "𝖓", "𝖔", "𝖕", "𝖖", "𝖗", "𝖘", "𝖙", "𝖚", "𝖛", "𝖜", "𝖝", "𝖞", "𝖟"
942    }, },
943    { "ss tf",        {
944        "đ–ș", "đ–»", "đ–Œ", "đ–œ", "đ–Ÿ", "𝖿", "𝗀", "𝗁", "𝗂", "𝗃", "𝗄", "𝗅", "𝗆", "𝗇", "𝗈", "𝗉", "𝗊", "𝗋", "𝗌", "𝗍", "𝗎", "𝗏", "𝗐", "𝗑", "𝗒", "𝗓",
945        "𝖠", "𝖡", "𝖱", "𝖣", "đ–€", "đ–„", "𝖩", "𝖧", "𝖹", "đ–©", "đ–Ș", "đ–«", "𝖬", "𝖭", "𝖼", "𝖯", "𝖰", "đ–±", "đ–Č", "𝖳", "𝖮", "đ–”", "đ–¶", "đ–·", "𝖾", "đ–č",
946        "𝟱", "𝟣", "đŸ€", "đŸ„", "𝟩", "𝟧", "𝟹", "đŸ©", "đŸȘ", "đŸ«"
947    }, },
948    { "ss bf",        {
949        "𝗼", "𝗯", "𝗰", "đ—±", "đ—Č", "𝗳", "𝗮", "đ—”", "đ—¶", "đ—·", "𝗾", "đ—č", "đ—ș", "đ—»", "đ—Œ", "đ—œ", "đ—Ÿ", "𝗿", "𝘀", "𝘁", "𝘂", "𝘃", "𝘄", "𝘅", "𝘆", "𝘇",
950        "𝗔", "𝗕", "𝗖", "𝗗", "𝗘", "𝗙", "𝗚", "𝗛", "𝗜", "𝗝", "𝗞", "𝗟", "𝗠", "𝗡", "𝗱", "𝗣", "đ—€", "đ—„", "𝗩", "𝗧", "𝗹", "đ—©", "đ—Ș", "đ—«", "𝗬", "𝗭",
951        "𝟬", "𝟭", "𝟼", "𝟯", "𝟰", "đŸ±", "đŸČ", "𝟳", "𝟮", "đŸ”",
952    }, },
953    { "ss it",        {
954        "𝘱", "𝘣", "đ˜€", "đ˜„", "𝘩", "𝘧", "𝘹", "đ˜©", "đ˜Ș", "đ˜«", "𝘬", "𝘭", "𝘼", "𝘯", "𝘰", "đ˜±", "đ˜Č", "𝘳", "𝘮", "đ˜”", "đ˜¶", "đ˜·", "𝘾", "đ˜č", "đ˜ș", "đ˜»",
955        "𝘈", "𝘉", "𝘊", "𝘋", "𝘌", "𝘍", "𝘎", "𝘏", "𝘐", "𝘑", "𝘒", "𝘓", "𝘔", "𝘕", "𝘖", "𝘗", "𝘘", "𝘙", "𝘚", "𝘛", "𝘜", "𝘝", "𝘞", "𝘟", "𝘠", "𝘡",
956    }, },
957    { "ss bi",        {
958        "𝙖", "𝙗", "𝙘", "𝙙", "𝙚", "𝙛", "𝙜", "𝙝", "𝙞", "𝙟", "𝙠", "𝙡", "𝙱", "𝙣", "đ™€", "đ™„", "𝙩", "𝙧", "𝙹", "đ™©", "đ™Ș", "đ™«", "𝙬", "𝙭", "𝙼", "𝙯",
959        "đ˜Œ", "đ˜œ", "đ˜Ÿ", "𝘿", "𝙀", "𝙁", "𝙂", "𝙃", "𝙄", "𝙅", "𝙆", "𝙇", "𝙈", "𝙉", "𝙊", "𝙋", "𝙌", "𝙍", "𝙎", "𝙏", "𝙐", "𝙑", "𝙒", "𝙓", "𝙔", "𝙕",
960    }, },
961    { "tt",           {
962        "𝚊", "𝚋", "𝚌", "𝚍", "𝚎", "𝚏", "𝚐", "𝚑", "𝚒", "𝚓", "𝚔", "𝚕", "𝚖", "𝚗", "𝚘", "𝚙", "𝚚", "𝚛", "𝚜", "𝚝", "𝚞", "𝚟", "𝚠", "𝚡", "𝚱", "𝚣",
963        "𝙰", "đ™±", "đ™Č", "𝙳", "𝙮", "đ™”", "đ™¶", "đ™·", "𝙾", "đ™č", "đ™ș", "đ™»", "đ™Œ", "đ™œ", "đ™Ÿ", "𝙿", "𝚀", "𝚁", "𝚂", "𝚃", "𝚄", "𝚅", "𝚆", "𝚇", "𝚈", "𝚉",
964        "đŸ¶", "đŸ·", "𝟾", "đŸč", "đŸș", "đŸ»", "đŸŒ", "đŸœ", "đŸŸ", "𝟿"
965    }, },
966    { "gr tf",        {
967        "α", "ÎČ", "Îł", "ÎŽ", "Δ", "ζ", "η", "Ξ", "Îč", "Îș", "λ", "ÎŒ", "Îœ", "Ο", "Îż", "π", "ρ", "ς", "σ", "τ", "υ", "φ", "χ", "ψ", "ω",
968        "Α", "Β", "Γ", "Δ", "Ε", "Ζ", "Η", "Θ", "Ι", "Κ", "Λ", "Μ", "Ν", "Ξ", "Ο", "Π", "ÎĄ", "Îą", "ÎŁ", "΀", "΄", "Ί", "Χ", "Κ", "Ω",
969    }, },
970    { "gr bf",        {
971        "𝛂", "𝛃", "𝛄", "𝛅", "𝛆", "𝛇", "𝛈", "𝛉", "𝛊", "𝛋", "𝛌", "𝛍", "𝛎", "𝛏", "𝛐", "𝛑", "𝛒", "𝛓", "𝛔", "𝛕", "𝛖", "𝛗", "𝛘", "𝛙", "𝛚",
972        "𝚹", "đš©", "đšȘ", "đš«", "𝚬", "𝚭", "𝚼", "𝚯", "𝚰", "đš±", "đšČ", "𝚳", "𝚮", "đš”", "đš¶", "đš·", "𝚾", "đšč", "đšș", "đš»", "đšŒ", "đšœ", "đšŸ", "𝚿", "𝛀",
973    }, },
974    { "gr it",        {
975        "đ›Œ", "đ›œ", "đ›Ÿ", "𝛿", "𝜀", "𝜁", "𝜂", "𝜃", "𝜄", "𝜅", "𝜆", "𝜇", "𝜈", "𝜉", "𝜊", "𝜋", "𝜌", "𝜍", "𝜎", "𝜏", "𝜐", "𝜑", "𝜒", "𝜓", "𝜔",
976        "𝛱", "𝛣", "đ›€", "đ›„", "𝛩", "𝛧", "𝛹", "đ›©", "đ›Ș", "đ›«", "𝛬", "𝛭", "𝛼", "𝛯", "𝛰", "đ›±", "đ›Č", "𝛳", "𝛮", "đ›”", "đ›¶", "đ›·", "𝛾", "đ›č", "đ›ș",
977    }, },
978    { "gr bi",        {
979        "đœ¶", "đœ·", "𝜾", "đœč", "đœș", "đœ»", "đœŒ", "đœœ", "đœŸ", "𝜿", "𝝀", "𝝁", "𝝂", "𝝃", "𝝄", "𝝅", "𝝆", "𝝇", "𝝈", "𝝉", "𝝊", "𝝋", "𝝌", "𝝍", "𝝎",
980        "𝜜", "𝜝", "𝜞", "𝜟", "𝜠", "𝜡", "𝜱", "𝜣", "đœ€", "đœ„", "𝜩", "𝜧", "𝜹", "đœ©", "đœȘ", "đœ«", "𝜬", "𝜭", "𝜼", "𝜯", "𝜰", "đœ±", "đœČ", "𝜳", "𝜮",
981    }, },
982    { "gr ss bf",     {
983        "𝝰", "đ±", "đČ", "𝝳", "𝝮", "đ”", "đ¶", "đ·", "𝝾", "đč", "đș", "đ»", "đŒ", "đœ", "đŸ", "𝝿", "𝞀", "𝞁", "𝞂", "𝞃", "𝞄", "𝞅", "𝞆", "𝞇", "𝞈",
984        "𝝖", "𝝗", "𝝘", "𝝙", "𝝚", "𝝛", "𝝜", "𝝝", "𝝞", "𝝟", "𝝠", "𝝡", "𝝱", "𝝣", "đ€", "đ„", "𝝩", "𝝧", "𝝹", "đ©", "đȘ", "đ«", "𝝬", "𝝭", "𝝼",
985    }, },
986    { "gr ss bi",  {
987        "đžȘ", "đž«", "𝞬", "𝞭", "𝞼", "𝞯", "𝞰", "đž±", "đžČ", "𝞳", "𝞮", "đž”", "đž¶", "đž·", "𝞾", "đžč", "đžș", "đž»", "đžŒ", "đžœ", "đžŸ", "𝞿", "𝟀", "𝟁", "𝟂",
988        "𝞐", "𝞑", "𝞒", "𝞓", "𝞔", "𝞕", "𝞖", "𝞗", "𝞘", "𝞙", "𝞚", "𝞛", "𝞜", "𝞝", "𝞞", "𝞟", "𝞠", "𝞡", "𝞱", "𝞣", "đž€", "đž„", "𝞩", "𝞧", "𝞹",
989    }, },
990    { "op", {
991    }, },
992    { "sy a", {
993    }, },
994    { "sy b", {
995    }, },
996    { "sy c", {
997    }, },
998}
999
1000local mathlists    = { }
1001local mathselector = { }
1002
1003for i=1,#mathsets do
1004    local mathset = mathsets[i]
1005    mathselector[#mathselector+1] = mathset[1]
1006    mathlists[mathset[1]] = mathset[2]
1007end
1008
1009local enabled   = 0
1010local usedlists = {
1011    { name = "text", current = "en", lists = textlists, selector = textselector },
1012    { name = "math", current = "tf", lists = mathlists, selector = mathselector },
1013}
1014
1015-- I haven't found out yet how to create a strip as in scite. 
1016
1017-- local function make_strip()
1018--     local used     = usedlists[enabled]
1019--     local lists    = used.lists
1020--     local alphabet = lists[used.current]
1021--     local selector = "(hide)(" .. concat(used.selector,")(") .. ")"
1022--     local alphabet = "(" .. used.current .. ":)(" .. concat(alphabet,")(") .. ")"
1023-- --     scite.StripShow(selector .. "\n" .. alphabet)
1024-- end
1025-- 
1026-- local function hide_strip()
1027-- --     scite.StripShow("")
1028-- end
1029-- 
1030-- local function process_strip(control)
1031-- --     local value = scite.StripValue(control)
1032-- --     if value == "hide" then
1033-- --         hide_strip()
1034-- --         return
1035-- --     elseif find(value,".+:") then
1036-- --         return
1037-- --     end
1038-- --     local used = usedlists[enabled]
1039-- --     if used.lists[value] then
1040-- --         used.current = value
1041-- --         make_strip()
1042-- --     else
1043-- --         editor:insert(editor.CurrentPos,value)
1044-- --     end
1045-- end
1046-- 
1047-- local function ignore_strip()
1048-- end
1049
1050function runner.unicodes(name)
1051--     enabled = enabled + 1
1052--     if usedlists[enabled] then
1053--         make_strip()
1054--     else
1055--         enabled = 0
1056--         hide_strip()
1057--     end
1058end
1059 
1060return runner
1061
1062-- The ui.print function is a bit heavy as each flush will parse the whole list of buffers.
1063-- Also it does some tab magic that we don't need or want. There is the original ui.print for
1064-- that. FWIW, speed is not an issue. Some optimizations:
1065
1066-- function _print(buffer_type,one,two,...)
1067--     ...
1068--     print_buffer:append_text(one)
1069--     if two then
1070--         print_buffer:append_text(two)
1071--         for i=1, select('#', ...) do
1072--             print_buffer:append_text((select(i,...)))
1073--         end
1074--     end
1075--     print_buffer:append_text('\n')
1076--     ...
1077-- end
1078--
1079-- And a better splitter:
1080--     ...
1081--     local rest
1082--     local function emit_output(output)
1083--         for line, lineend in output:gmatch('([^\r\n]+)([\r\n]?)') do
1084--             if rest then
1085--                 line = rest .. line
1086--                 rest = nil
1087--             end
1088--             if lineend and lineend ~= "" then
1089--                 events.emit(event, line, ext_or_lexer)
1090--             else
1091--                 rest = line
1092--             end
1093--         end
1094--     end
1095--     ...
1096--         if rest then
1097--             events.emit(event,rest,ext_or_lexer)
1098--         end
1099--         events.emit(event, '> exit status: '..status)
1100--     ...
1101