diff options
Diffstat (limited to 'context/data/textadept/context/modules/textadept-context-runner.lua')
-rw-r--r-- | context/data/textadept/context/modules/textadept-context-runner.lua | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/context/data/textadept/context/modules/textadept-context-runner.lua b/context/data/textadept/context/modules/textadept-context-runner.lua new file mode 100644 index 000000000..4d0053996 --- /dev/null +++ b/context/data/textadept/context/modules/textadept-context-runner.lua @@ -0,0 +1,275 @@ +local info = { + version = 1.002, + comment = "prototype textadept runner for context/metafun", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", +} + +-- This is an adapted version of the run code by mitchell.att.foicica.corunner. The main +-- reason I started patching is that long lines got broken in the middle so we needed +-- to have a more clever line splitter that saves half of a line for later. Then I +-- decided to come up with a few more variants so in the end ... it's just too tempting +-- make something that exactly suits out needs. In fact, maybe I'll do that some day: +-- take core textadept and make a dedicated variant for the kind of processing that we +-- do and make it suitable for document authors (the manual says that is doable). In that +-- case I can also use a lot of already written helpers. +-- +-- The error scanner is not needed. If I need one, it will be using a lexers applied +-- afterwards because working on half lines is not going to work out well anyway. +-- +-- Here I removed iconv calls as in context we use utf (less hassle with fonts too). One +-- can always use the original approach. +-- +-- The events seems to have hard coded names, Also, the name of the message buffer cannot +-- be changes because otherwise we get a message when the session is restored. I don't +-- care about locales. +-- +-- Somehow the process hangs when I refresh the pdf viewer, this doesn't happen in scite so +-- the underlying code is for the moment less reliant. + +local match, gsub, find, format = string.match, string.gsub, string.find, string.format +local assert, type = assert, type + +local original = textadept.run +local runner = { } + +runner.MARK_WARNING = original.MARK_WARNING +runner.MARK_ERROR = original.MARK_ERROR + +local specifications = { } +runner.specifications = specifications + +----- RUNNER_EVENT = "[Context Runner]" +local OUTPUT_BUFFER = '[Message Buffer]' -- CONSOLE + +----- events.RUNNER_EVENT = RUNNER_EVENT + +local currentprocess = nil +local xbuffer = nil + +local function find_buffer(buffer_type) + for i=1,#_BUFFERS do + local buffer = _BUFFERS[i] + if buffer._type == buffer_type then + return buffer + end + end +end + +local function print_output(str) + local print_buffer = find_buffer(OUTPUT_BUFFER) + -- some simplified magic copied from the adeptext runner + if not print_buffer then + if not ui.tabs then + view:split() + end + print_buffer = buffer.new() + print_buffer._type = OUTPUT_BUFFER + events.emit(events.FILE_OPENED) + else + for i=1,#_VIEWS do + local view = _VIEWS[i] + if view.buffer._type == OUTPUT_BUFFER then + ui.goto_view(view) + break + end + end + if view.buffer._type ~= OUTPUT_BUFFER then + view:goto_buffer(print_buffer) + end + end + print_buffer:append_text(str) + print_buffer:goto_pos(buffer.length) + print_buffer:set_save_point() + -- + return true -- quits +end + +local function clear_output() + xbuffer = buffer + local print_buffer = find_buffer(OUTPUT_BUFFER) + if print_buffer then + print_buffer:clear_all() + end +end + +local function is_output(buffer) + return buffer._type == OUTPUT_BUFFER +end + +-- Instead of events we will have out own interceptors so that we don't have +-- interference. The main problem is that we don't hav emuch control over the +-- order. If we have much actions I can always come up with something. + +local function process(buffer,filename,action) + if not filename then + filename = buffer.filename + end + if filename == buffer.filename then + buffer:annotation_clear_all() -- needed ? + io.save_file() + end + if filename == "" then + return + end + local suffix = match(filename,'[^/\\.]+$') + local specification = specifications[suffix] + if not specification then + return + end + local action = specification[action] + local quitter = nil + if type(action) == "table" then + action = action.command + quitter = action.quitter + end + if type(action) ~= "string" then + return + end + clear_output() + local pathpart = '' + local basename = filename + if find(filename,'[/\\]') then + pathpart, basename = match(filename,'^(.+[/\\])([^/\\]+)$') + end + -- beter strip one from the end + local nameonly = match(basename,'^(.+)%.') + -- more in sync which what we normally do (i'd rather use the ctx template mechanism) + local command = gsub(action,'%%(.-)%%', { + filename = filename, + pathname = dirname, + dirname = dirname, + pathpart = dirname, + basename = basename, + nameonly = nameonly, + suffix = suffix, + selection = function() return match(buffer.get_sel_text(),"%s*([A-Za-z]+)") end, + }) + -- for fun i'll add a ansi escape sequence lexer some day + local function emit_output(output) + print_output(output) -- events.emit(RUNNER_EVENT,...) + -- afaik there is no way to check if we're waiting for input (no input callback) + if quitter then + local quit, message = quitter(interceptor) + if quit then + if message then + print_output(format("\n\n> quit: %s\n",message)) + end + runner.quit() + end + end + end + local function exit_output(status) + print_output(format("\n\n> exit: %s, press esc to return to source\n",status)) -- events.emit(RUNNER_EVENT,...) + end + print_output(format("> command: %s\n",command)) -- events.emit(RUNNER_EVENT,...) + currentprocess = assert(spawn(command, pathpart, emit_output, emit_output, exit_output)) +end + +function runner.install(name) + return function(filename) + process(buffer,filename,name) + end +end + +runner.check = runner.install("check") +runner.process = runner.install("process") +runner.preview = runner.install("preview") + +function runner.resultof(command) -- from l-os.lua + local handle = io.popen(command,"r") + if handle then + local result = handle:read("*all") or "" + handle:close() + return result + else + return "" + end +end + +function runner.quit() + if currentprocess then + assert(currentprocess:kill()) + end +end + +local function char_added(code) + if code == 10 and currentprocess and currentprocess:status() == 'running' and buffer._type == OUTPUT_BUFFER then + local line_num = buffer:line_from_position(buffer.current_pos) - 1 + currentprocess:write((buffer:get_line(line_num))) + end + return true -- quits +end + +function runner.goto_error(line, next) + -- see original code for how to do it +end + +local function key_press(code) + if xbuffer and keys.KEYSYMS[code] == 'esc' then + view:goto_buffer(xbuffer) + return true + end +end + +local function double_click() + if xbuffer and is_output(buffer) then + view:goto_buffer(xbuffer) + return true + end +end + +-- Tricky: we can't reset an event (because we need to know the function which is +-- local. So, a first solution injected a false into the table which will trigger +-- a break and then I found out that returning true has the same effect. Then I +-- found out that we can have our own events and next decided not to use them at +-- all. + +-- events.connect(events.RUNNER_EVENT, print_output, 1) + +events.connect(events.CHAR_ADDED, char_added, 1) +events.connect(events.KEYPRESS, key_press, 1) +events.connect(events.DOUBLE_CLICK, double_click, 1) + +return runner + +-- The ui.print function is a bit heavy as each flush will parse the whole list of buffers. +-- Also it does some tab magic that we don't need or want. There is the original ui.print for +-- that. FWIW, speed is not an issue. Some optimizations: + +-- function _print(buffer_type,one,two,...) +-- ... +-- print_buffer:append_text(one) +-- if two then +-- print_buffer:append_text(two) +-- for i=1, select('#', ...) do +-- print_buffer:append_text((select(i,...))) +-- end +-- end +-- print_buffer:append_text('\n') +-- ... +-- end +-- +-- And a better splitter: +-- ... +-- local rest +-- local function emit_output(output) +-- for line, lineend in output:gmatch('([^\r\n]+)([\r\n]?)') do +-- if rest then +-- line = rest .. line +-- rest = nil +-- end +-- if lineend and lineend ~= "" then +-- events.emit(event, line, ext_or_lexer) +-- else +-- rest = line +-- end +-- end +-- end +-- ... +-- if rest then +-- events.emit(event,rest,ext_or_lexer) +-- end +-- events.emit(event, '> exit status: '..status) +-- ... |