build.lua (6112B)
1 local gf = require('goto-ref') 2 local util = require('util') 3 4 vis.events.subscribe(vis.events.FILE_SAVE_PRE, function(file) 5 local M = require('plugins/vis-lint') 6 M.logger = function(str, level) 7 if level == M.log.ERROR then 8 vis:message(str) 9 end 10 end 11 M.fixers["bibtex"] = { "bibtidy" } 12 M.fixers["c"] = { "clang-format -fallback-style=none" } 13 M.fixers["cpp"] = { "clang-format -fallback-style=none" } 14 M.fixers["json"] = { "jq --tab" } 15 return M.fix(file) 16 end) 17 18 local logger = function(clear, ostr, estr) 19 if (ostr == nil or #ostr == 0) and (estr == nil or #estr == 0) then return end 20 if clear then util.message_clear(vis) end 21 if ostr and #ostr ~= 0 then vis:message(ostr) end 22 if estr and #estr ~= 0 then vis:message(estr) end 23 vis:message(string.rep("=", vis.win.viewport.width / 2)) 24 end 25 26 local file_exists = function(file) 27 local f = io.open(file, "r") 28 local result = f ~= nil 29 if result then io.close(f) end 30 return result 31 end 32 33 local standard_error_search = function(error_string) 34 gf.setup_iterators_from_text(error_string, function(str) 35 return str:find(" error:") or 36 str:find(": note:") or 37 str:find(": warning:") 38 end) 39 end 40 41 local build_c_cmd = function() 42 local cmd 43 if cmd == nil and file_exists('./build') then cmd = '$PWD/build --debug' end 44 if cmd == nil and file_exists('./build.sh') then cmd = '$PWD/build.sh' end 45 if cmd == nil and file_exists('Makefile') then cmd = 'make' end 46 return cmd 47 end 48 49 local build_c_response = function(code, out_string, error_string) 50 logger(true, nil, error_string) 51 standard_error_search(error_string) 52 return true 53 end 54 55 local build_tex = function(file) 56 local cmd = "lualatex -halt-on-error -shell-escape " 57 58 -- build in draft mode to update references 59 local err, ostr = vis:pipe(cmd .. "-draftmode " .. file.name) 60 if err ~= 0 then logger(true, ostr) return false end 61 62 local fp = util.splitext(file.name) 63 -- update refrences 64 vis:command("!biber " .. fp .. " >/dev/null") 65 -- update nomenclature 66 -- vis:command("!makeindex " .. fp .. ".nlo -s nomencl.ist -o " .. fp .. ".nls >/dev/null") 67 -- update glossary 68 -- vis:command("!makeglossaries " .. fp .. " >/dev/null") 69 70 -- build actual pdf 71 err = vis:pipe(cmd .. file.name) 72 if err ~= 0 then return false end 73 74 -- reload pdf (zathura does this automatically) 75 -- vis:command('!pkill -HUP mupdf') 76 77 return true 78 end 79 80 local run_python_cmd = function(file) return 'python ' .. file.name end 81 local run_python_response = function(code, out_string, error_string) 82 logger(true, out_string, error_string) 83 if error_string ~= '' then return false end 84 return true 85 end 86 87 local run_sh_cmd = function(file) return "$PWD/" .. file.name end 88 local run_sh_response = function(code, out_string, error_string) 89 logger(true, out_string, error_string) 90 standard_error_search(error_string) 91 return true 92 end 93 94 local current_build_id = 0 95 local current_build 96 vis:command_register("build_kill", function() 97 current_build = nil 98 end, "kill currently running build") 99 100 vis.events.subscribe(vis.events.PROCESS_RESPONSE, function(name, event, code, message) 101 if not current_build or current_build.name ~= name then 102 return 103 end 104 105 if event == "STDOUT" then 106 current_build.out = current_build.out .. message 107 end 108 109 if event == "STDERR" then 110 current_build.error = current_build.error .. message 111 end 112 113 if event == "EXIT" or event == "SIGNAL" then 114 local build = current_build 115 current_build = nil 116 117 local result, info = build.response(code, build.out, build.error) 118 local s = "build: completed (" .. code .. ")" 119 if info then s = s .. " | info: " .. info end 120 if result == true then vis:info(s) end 121 end 122 end) 123 124 local launch_command = function(command, response_handler) 125 if current_build then 126 vis:info("build already running; :build_kill to kill") 127 return false 128 end 129 130 vis:command('X/.*/w') 131 local name = 'build{' .. current_build_id .. '}' 132 current_build_id = current_build_id + 1 133 local build_fd = vis:communicate(name, command) 134 current_build = {name = name, fd = build_fd, out = '', error = '', response = response_handler} 135 vis:info("build: starting: ".. command) 136 end 137 138 vis.events.subscribe(vis.events.WIN_OPEN, function(win) 139 local lang = { 140 bash = {command = run_sh_cmd, response = run_sh_response }, 141 rc = {command = run_sh_cmd, response = run_sh_response }, 142 c = {command = build_c_cmd, response = build_c_response }, 143 cpp = {command = build_c_cmd, response = build_c_response }, 144 python = {command = run_python_cmd, response = run_python_response}, 145 latex = build_tex, 146 } 147 148 local builder = lang[win.syntax] 149 local binding, command 150 if builder ~= nil and type(builder) == 'function' then 151 binding = function() builder(win.file) end 152 elseif builder ~= nil then 153 if builder ~= nil then command = builder.command(win.file) end 154 if command == nil then 155 binding = function() vis:info("build: missing command for filetype: " .. win.syntax) end 156 else 157 binding = function() launch_command(command, builder.response) end 158 end 159 end 160 win:map(vis.modes.NORMAL, " c", binding, "build file in current window") 161 end) 162 163 local cached_command 164 vis:command_register("build", function(argv) 165 if #argv == 0 and cached_command == nil then vis:info("build cmd [arg ...]") return false end 166 167 if #argv ~= 0 then cached_command = table.concat(argv, " ") end 168 launch_command(cached_command, function(code, out_string, error_string) 169 if code ~= 0 then 170 logger(true, ostr, estr) 171 standard_error_search(estr) 172 end 173 return true 174 end) 175 end, "run command and try to collect errors") 176 177 vis:command_register("todo", function() 178 local _, out = vis:pipe('ag --depth=0 --count "(FIXME|TODO)"') 179 local count = 0 180 if out then 181 local file_count_table = gf.generate_line_indices(out) 182 for i = 1, #file_count_table do 183 local file, occurences = table.unpack(file_count_table[i]) 184 count = count + tonumber(occurences) 185 end 186 end 187 if count ~= 0 then 188 local _, out = vis:pipe('ag --depth=0 "(FIXME|TODO)"') 189 logger(true, out) 190 standard_error_search(out) 191 end 192 vis:info("Found TODOs: " .. tostring(count)) 193 end, "search for TODOs in codebase")