local Job = require('plenary.job') function make(opts) local makeprg = vim.bo.makeprg local errorformat = vim.bo.errorformat if makeprg == '' then return end -- escape special characters in args args = string.gsub(opts.args or '', '%%', '%%%%') -- substitute $* makeprg, _ = string.gsub(makeprg, '%$%*', args); -- expand makeprg = vim.fn.expandcmd(makeprg) local function on_exit(job, retval) local result = {} vim.list_extend(result, job:result()) vim.list_extend(result, job:stderr_result()) vim.notify(string.format( ':!%s\n%s\n\nshell returned %d', makeprg, table.concat(result, '\n'), retval )) vim.fn.setqflist({}, ' ', { title = makeprg, lines = result, efm = errorformat, }) vim.api.nvim_exec_autocmds('QuickFixCmdPost', {}) if #result > 0 then vim.cmd('copen') end end Job:new({ command = 'sh', args = { '-c', makeprg }, on_exit = vim.schedule_wrap(on_exit), }):start() end vim.api.nvim_create_user_command('Make', make, { nargs = '*', complete = 'file', }) function ensure_log_buffer(bufname) for _, buf in ipairs(vim.api.nvim_list_bufs()) do if vim.api.nvim_buf_is_loaded(buf) and vim.api.nvim_buf_get_name(buf) == bufname then vim.bo[buf].modifiable = true vim.api.nvim_buf_set_lines(buf, 0, -1, true, {}) vim.bo[buf].modifiable = false return buf end end local bufnr = vim.api.nvim_create_buf(true, true) vim.api.nvim_buf_set_name(bufnr, bufname) vim.bo[bufnr].filetype = 'git' vim.bo[bufnr].buftype = 'nofile' vim.bo[bufnr].bufhidden = 'hide' vim.bo[bufnr].swapfile = false vim.bo[bufnr].modifiable = false vim.api.nvim_win_set_buf(0, bufnr) -- avoid ui thrashing vim.opt_local.number = false vim.opt_local.relativenumber = false -- add fugitive mappings vim.fn['fugitive#MapJumps']() return bufnr end function git_lg(opts) local argstr = '' if #opts.args > 0 then argstr = ' ' .. opts.args end -- we want a pseudo fqdn so that it's not considered relative to the current -- directory local bufname = 'cmd://git//lg' .. argstr local bufnr = ensure_log_buffer(bufname) local job -- there are custom syntax highlights for --graph and --pretty local args = { 'log', '--abbrev-commit', '--graph', '--pretty=%h %d %s (%cr) <%an>', } vim.list_extend(args, opts.fargs) local stdout_lines = {} local stderr_lines = {} local first = true local append_to_buf = vim.schedule_wrap(function(lines) -- stop appending and terminate the job if the buffer is closed if not vim.api.nvim_buf_is_loaded(bufnr) then job:shutdown(0, 0) return end -- the first append should replace the first line, succeeding appends -- should append to the end local start = -1 if first then start = 0 first = false end vim.bo[bufnr].modifiable = true vim.api.nvim_buf_set_lines(bufnr, start, -1, true, lines) vim.bo[bufnr].modifiable = false end) local function on_stdout(error, data) if data == nil then return end -- we get called for every line of output so buffer the lines to avoid too -- much overhead table.insert(stdout_lines, data) if #stdout_lines > 100 then local done = stdout_lines stdout_lines = {} append_to_buf(done) end end local function on_exit(job, retval) append_to_buf(stdout_lines) if retval ~= 0 then vim.schedule(function() vim.notify(string.format( ':%s\n%s\n\nreturned %d', 'GLg' .. argstr, table.concat(stderr_lines, '\n'), retval )) end) end end job = Job:new({ command = 'git', args = args, enable_recording = false, on_stdout = on_stdout, on_stderr = function(error, data) if data == nil then return end table.insert(stderr_lines, data) end, on_exit = on_exit, }) job:start() end vim.api.nvim_create_user_command('GLg', git_lg, { nargs = '*', complete = vim.fn['fugitive#LogComplete'], })