dotfiles

personal dotfiles
git clone anongit@rnpnr.xyz:dotfiles.git
Log | Files | Refs | Feed | Submodules

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")