require("vis") local M = {} M.par_prg = "par" M.par_tw = 72 M.ex_args = function () return "RET4bhgqw" .. M.par_tw end M.debug = false -- ex_args = "RET4bhgqB=.,?_A_aQ=_s>|w" .. par_tw -- export PARINIT='Tbp2hgqR B=.,?_A_a Q=_s>|' function print(...) if M.debug then _print_ = _print_ or io.open("/tmp/visprint", "w") _print_:write(..., "\n") end end --- Dump value of a variable in a formatted string -- --- @param o table Dumpable object --- @param tbs string|nil Tabulation string, ' ' by default --- @param tb number|nil Initial tabulation level, 0 by default --- @return string local function dump(o, tbs, tb) tb = tb or 0 tbs = tbs or " " if type(o) == "table" then local s = "{" if (next(o)) then s = s .. "\n" else return s .. "}" end tb = tb + 1 for k, v in pairs(o) do if type(k) ~= "number" then k = '"' .. k .. '"' end s = s .. tbs:rep(tb) .. "[" .. k .. "] = " .. dump(v, tbs, tb) s = s .. ",\n" end tb = tb - 1 return s .. tbs:rep(tb) .. "}" else return tostring(o) end end local supress_stdout = " >/dev/null" local supress_stderr = " 2>/dev/null" local supress_output = supress_stdout .. supress_stderr -- Return nil or a string of formatted text from a specific file range or text -- by calling the formatter command. -- If given a range we will use vis:pipe to get the output from the formatter. -- If a string was passed we call the formatter ourself and redirect its stdout -- to a temporary file. See http://lua-users.org/lists/lua-l/2007-10/msg00189.html. local function run_shell_cmd(cmd, params, inp) local out print("cmd = " .. cmd .. ", params = " .. params .. ", inp = " .. inp .. ", (" .. type(inp) .. ")") if type(inp) == "string" then local full_cmd = cmd .. " '" .. params .. "'" local tmp_name = os.tmpname() print("tmp_name = " .. tmp_name) full_cmd = full_cmd .. " > " .. tmp_name print("full_cmd = " .. full_cmd) local proc = assert(io.popen(full_cmd, "w")) print("proc = " .. type(proc)) proc:write(inp) -- this error detection may need lua5.2 proc:flush() local exit_code = proc:close() print("exit_code = " .. tostring(exit_code)) local tmp_file = assert(io.open(tmp_name, "r")) out = tmp_file:read("*a") tmp_file:close() -- print("out = " .. out) os.remove(tmp_name) if not exit_code then print("calling " .. full_cmd .. " failed (" .. out .. ")") return nil end else local range = inp local ret, so, _ = vis:pipe(vis.win.file, range, cmd) if ret ~= 0 then print("calling " .. cmd .. " failed (" .. ret .. ")") return nil end out = so end return out end function lines(str) local t = {} local function helper(line) table.insert(t, line) return "" end helper((str:gsub("(.-)\r?\n", helper))) return t end getmetatable(_VERSION).__index.lines = lines function hanging_symbol(line, nspc) local beg, fin = 0, 0 -- itemized list beg, fin = line:find("^[-+*] ", nspc) if beg then print("item beg = " .. beg .. ", fin = " .. fin) return fin end local beg, fin = line:find("^%d+%. ", nspc) if beg then print("enum beg = " .. beg .. ", fin = " .. fin) return fin end return nspc -- no hanging idennt end function new_segment(segment, p_arg) if next(segment) then print("M.par_prg = " .. M.par_prg .. ", M.ex_args = " .. M.ex_args() .. ", p_arg = " .. p_arg) local new_text = run_shell_cmd(M.par_prg, M.ex_args() .. "p" .. p_arg, table.concat(segment, "\n")) print("new_text = " .. new_text) return new_text, {}, 0 -- add-to-out, segment, p_arg else return "", {}, 0 -- add-to-out, segment, p_arg end end vis:operator_new( "gq", function(file, range, pos) local out = "" local hang_sym = "" local segment = {} local p_arg = 0 local range_lines = file:content(range):lines() for _, line in ipairs(range_lines) do local beg, beg_spc_len = line:find("^%s*") assert(beg == 1, "There is no way this would not be 1.") local non_spc_idx = line:len() >= beg_spc_len + 1 and beg_spc_len + 1 or 0 print("non_spc_idx = " .. non_spc_idx) hang_sym = hanging_symbol(line, non_spc_idx) print("hang_sym = " .. hang_sym .. ", non_spc_idx = " .. non_spc_idx) if non_spc_idx ~= 0 then -- non-empty line -- item: start new segment if hang_sym ~= non_spc_idx then seg_pared, segment, p_arg = new_segment(segment, p_arg) if seg_pared then out = out .. seg_pared p_arg = hang_sym end table.insert(segment, line) else -- continuous line in a segment, just collect it table.insert(segment, line) end else -- this is an empty line seg_pared, segment, p_arg = new_segment(segment, p_arg) if seg_pared then out = out .. seg_pared end end end if out:len() == 0 then print("Cannot process paragraph.") else file:delete(range) file:insert(range.start, out) end return range.start -- new cursor location end, "Formating operator, filter range through formatting filter" ) vis:option_register( "autoformat", "bool", function(value, toogle) if not vis.win then return false end vis.win.autoformat = toogle and not vis.win.autoformat or value print("Option autoformat = " .. tostring(vis.win.autoformat)) return true end, "Automatically format current paragraph." ) return M