local M = { current_jobs = {}, } local strip_ansi = function(line) line, _ = line:gsub(string.char(27) .. '[[0-9;]*m', '') return line end local setqf = function(opts, pid, data) if data == nil then return end vim.fn.setqflist({}, 'a', { title = opts.format_title, lines = { strip_ansi(data) }, efm = '%m', context = { cmd = { opts.command, unpack(opts.args) }, pid = pid, source = 'jobstart', }, }) vim.cmd.doautocmd('QuickFixCmdPost') end --create a new job and register --@return ~/.local/share/nvim/site/pack/packer/start/plenary.nvim/lua/plenary/job.lua function M.jobstart(opts) opts = opts or {} opts = vim.tbl_extend('keep', opts or {}, { enable_spinner = vim.g.job_enable_spinner, populate_quickfix = vim.g.job_populate_quickfix, attach = vim.g.job_attach, }) -- vim.notify_once(vim.inspect(opts), vim.log.levels.DEBUG) if opts.attach then return vim.schedule_wrap(function() vim.cmd.terminal({ args = { opts.command, unpack(opts.args) } }) end)() end local spinner = {} if opts.enable_spinner then local ok = false ok, spinner = pcall(require, 'spinner.core') if not ok then opts.enable_spinner = false end end if opts.format_title == nil then opts.format_title = table.concat({ opts.command, unpack(opts.args) }, ' ') elseif type(opts.format_title) == 'function' then opts.format_title = opts.format_title(opts.command, opts.args) elseif type(opts.format_title) == 'string' then -- noop else error(string.format('Invalid field "format_title" of type %s', type(opts.format_title))) end opts.on_start = vim.schedule_wrap(function(j) M.current_jobs[j.pid] = j if opts.populate_quickfix then vim.fn.setqflist({}, 'r') -- clear quickfix end if opts.enable_spinner then spinner.on_attach(j.pid, opts.format_title, vim.fn.bufnr()) spinner.on_progress('begin', j.pid, j.pid) end end) opts.on_exit = vim.schedule_wrap(function(j, code) M.current_jobs[j.pid] = nil if not (code == 0) then vim.notify(string.format('[%d] exit %d', j.pid, code), vim.log.levels.ERROR) end if opts.enable_spinner then spinner.on_progress('end', j.pid, j.pid) spinner.on_exit(nil, nil, j.pid) end if opts.populate_quickfix then vim.cmd.doautocmd('QuickFixCmdPost') end end) opts.on_stdout = vim.schedule_wrap(function(err, data, j) if err ~= nil then vim.notify(string.format('[%d] error: %s', j.pid, err), vim.log.levels.ERROR) end -- handle error? if opts.populate_quickfix and opts.populate_quickfix ~= 'onerror' then setqf(opts, j.pid, data) end end) opts.on_stderr = vim.schedule_wrap(function(err, data, j) if err ~= nil then vim.notify(string.format('[%d] error: %s', j.pid, err), vim.log.levels.ERROR) end -- handle error? if opts.populate_quickfix then setqf(opts, j.pid, data) end end) -- run vim.fn.expand() on all args for i = 1, #opts.args do opts.args[i] = vim.fn.expandcmd(opts.args[i]) end local j = require('plenary.job'):new(opts) j:start() return j end function M.sh(command, opts) return M.jobstart(vim.tbl_extend('keep', opts or {}, { command = vim.env.SHELL, args = { '-c', command }, -- strip 'sh -c' from the title (used by progress reporting etc.) format_title = function(_, args) return table.concat({ unpack(args, 2, #args) }, ' ') end, })) end function M.make(opts) opts = opts or {} local makeprg = vim.fn.expandcmd(vim.opt.makeprg:get()) local cwd = vim.fn.expand('%:p:h') if not (string.match(makeprg, 'make') == nil) then -- detect makefile opts.cwd = require('lspconfig.util').root_pattern('Makefile')(cwd) end return M.sh(makeprg, opts) end function M.list() require('telescope._extensions').manager['jobs']['jobs']() end function M.setup(opts) opts = vim.tbl_extend('keep', opts, { telescope = true, mappings = true }) -- defaults vim.g.job_enable_spinner = true vim.g.job_populate_quickfix = 'onerror' vim.g.job_attach = false if opts.telescope then require('telescope').load_extension('jobs') vim.api.nvim_create_user_command('Jobs', M.list, { desc = 'list jobs managed by internal.job' }) if opts.mappings then vim.keymap.set('n', 'jl', M.list, { desc = 'list jobs managed by internal.job' }) end end vim.api.nvim_create_user_command('Make', function(a) require('internal.job').make(a.fargs) end, { nargs = '*', desc = 'run make target or &makeprg with internal.job' }) vim.api.nvim_create_user_command('Sh', function(a) require('internal.job').sh(table.concat(a.fargs, ' ')) end, { nargs = '*', desc = 'run shell command with internal.job' }) if opts.mappings then vim.keymap.set('n', 'jm', M.make, { desc = 'run &makeprg with internal.job' }) vim.keymap.set('n', 'js', ':Sh ', { desc = 'run shell commands with internal.job' }) end end return M