diff options
| author | Robert Günzler <r@gnzler.io> | 2026-09-27 17:38:39 +0200 |
|---|---|---|
| committer | Robert Günzler <r@gnzler.io> | 2026-09-27 17:38:39 +0200 |
| commit | 0860db73ae6fd9a6f6347219d820bf026a6b8b91 (patch) | |
| tree | 11318c3670d1c32c5aa0bf404c22fba3bf63fc53 /.config/nvim/lua/mod | |
| parent | 4e34cbf9673aeaba252e01df554a0d204e83c5e2 (diff) | |
| download | dotfiles-master.tar.gz | |
Automated-commit-by: dots Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to '.config/nvim/lua/mod')
| -rw-r--r-- | .config/nvim/lua/mod/gh.lua | 86 | ||||
| -rw-r--r-- | .config/nvim/lua/mod/git.lua | 23 | ||||
| -rw-r--r-- | .config/nvim/lua/mod/jobs.lua | 68 | ||||
| -rw-r--r-- | .config/nvim/lua/mod/termopen.lua | 102 | ||||
| -rw-r--r-- | .config/nvim/lua/mod/utils.lua | 15 | ||||
| -rw-r--r-- | .config/nvim/lua/mod/win.lua | 110 |
6 files changed, 404 insertions, 0 deletions
diff --git a/.config/nvim/lua/mod/gh.lua b/.config/nvim/lua/mod/gh.lua new file mode 100644 index 0000000..1f8a267 --- /dev/null +++ b/.config/nvim/lua/mod/gh.lua @@ -0,0 +1,86 @@ +local M = {} + +-- gh api \ +-- --method POST \ +-- -H "Accept: application/vnd.github+json" \ +-- -H "X-GitHub-Api-Version: 2022-11-28" \ +-- /repos/OWNER/REPO/pulls/PULL_NUMBER/comments \ + +function M.pr_comment(repo, pull, commit_id, comment) + vim.print(vim.inspect(comment)) + vim.print(vim.inspect({ + 'gh', + 'api', + '--method', + 'POST', + string.format('/repos/%s/pulls/%d/comments', repo, pull), + '-f', + 'body=' .. comment.body, + '-f', + 'commit_id=' .. commit_id, + '-f', + 'path=' .. comment.path, + '-f', + 'side=RIGHT', + '-f', + 'line=' .. comment.line, + })) +end + +function M.pr_view() + local p = vim + .system({ + 'gh', + 'pr', + 'view', + '--json', + 'number,headRepository,headRefOid', + '--jq', + '{head:.headRefOid,repo:.headRepository.nameWithOwner,pull:.number}', + }) + :wait() + return vim.json.decode(p.stdout) +end + +function M.submit_review() + local gitroot = vim.fs.root(0, { '.git' }) + if not gitroot then + vim.notify('Not a git repository.', vim.log.levels.WARN) + return + end + + local review = { comments = {} } + + local marks = require('haunt.api').get_bookmarks() + for _, mark in ipairs(marks) do + -- nil for bookmarks outside the current repo + local path = vim.fs.relpath(gitroot, mark.file) + if path then + review.comments[#review.comments] = { + path = path, + line = mark.line, + body = mark.note, + } + end + end + + review.pr = M.pr_view() + + vim.print(vim.inspect(review)) + + vim.ui.select({ + 'OK', + 'Cancel', + }, { + prompt = 'Submit review?', + }, function(choice) + vim.print(choice) + if choice == 'OK' then + for _, comment in ipairs(review.comments) do + M.pr_comment(pr.repo, pr.pull, pr.head, comment) + end + end + end) +end + +return M diff --git a/.config/nvim/lua/mod/git.lua b/.config/nvim/lua/mod/git.lua new file mode 100644 index 0000000..3e36e61 --- /dev/null +++ b/.config/nvim/lua/mod/git.lua @@ -0,0 +1,23 @@ +local M = {} + +function M.commit() + local commit = vim.fn.expand('<cword>') -- XXX: can we do better? + vim.cmd.DiffviewOpen(commit .. '^!') +end + +function M.log() + local bufnr = vim.api.nvim_create_buf(true, true) + assert(bufnr ~= 0, 'Failed to create buffer') + + vim.bo[bufnr].bufhidden = 'wipe' + vim.bo[bufnr].modifiable = true + vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, content) + vim.bo[bufnr].modifiable = false + + vim.keymap.set('n', 'q', '<cmd>quit!<cr>', { silent = true, buffer = bufnr }) + vim.keymap.set('n', 'K', M.commit, { silent = true, buffer = bufnr }) + + vim.api.nvim_win_set_buf(0, bufnr) +end + +return M diff --git a/.config/nvim/lua/mod/jobs.lua b/.config/nvim/lua/mod/jobs.lua new file mode 100644 index 0000000..a4dea6f --- /dev/null +++ b/.config/nvim/lua/mod/jobs.lua @@ -0,0 +1,68 @@ +-- this plugin provides an API to register and recall jobs. + +local M = { + _jobs = {}, +} + +local function format_jobcmd(cmd) + if type(cmd) == 'table' then + return table.concat(cmd, ' ') + end + return cmd +end + +local function register(id, cmd) + table.insert(M._jobs, id, cmd) + vim.notify_once(string.format('started job %d (%s)', id, format_jobcmd(M._jobs[id]))) +end + +local function deregister(id) + local cmd = M._jobs[id] + table.insert(M._jobs, id, nil) + vim.notify_once(string.format('stopped job %d (%s)', id, format_jobcmd(cmd))) +end + +function M.start(opts) + local previous_on_exit = opts.on_exit or function() end + opts.on_exit = function(id, exitcode, event) + deregister(id) + return previous_on_exit(id, exitcode, event) + end + + local id = vim.fn.jobstart(opts.cmd, opts) + + register(id, opts.cmd) +end + +function M.select() + if #M._jobs == 0 then + vim.notify('No running jobs', vim.log.levels.WARN) + return + end + + local function provider(cb) + for id, cmd in pairs(M._jobs) do + cb(string.format('[%d] %s', id, format_jobcmd(cmd))) + end + cb(nil) + end + local function parse_jobid(entry) + return tonumber(string.match(entry, '%[([0-9]*)%]')) + end + + require('fzf-lua').fzf_exec(provider, { + prompt = 'Jobs❯', + actions = { + ['ctrl-x'] = { + fn = function(selected) + for _, entry in pairs(selected) do + vim.fn.jobstop(parse_jobid(entry)) + end + end, + reload = true, + }, + }, + }) +end + +return M diff --git a/.config/nvim/lua/mod/termopen.lua b/.config/nvim/lua/mod/termopen.lua new file mode 100644 index 0000000..f8117f1 --- /dev/null +++ b/.config/nvim/lua/mod/termopen.lua @@ -0,0 +1,102 @@ +-- this plugin provides a popup terminal. +local jobs = require('mod.jobs') + +local M = {} + +function M.termopen(opts) + opts = opts or {} + + -- setup the buffer + local buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_set_option_value('bufhidden', 'wipe', { buf = buf }) + vim.api.nvim_set_option_value('modifiable', false, { buf = buf }) + + -- setup keymappings + vim.keymap.set('n', 'q', '<cmd>close<cr>', { buffer = buf }) + vim.keymap.set('n', '<esc>', '<cmd>close<cr>', { buffer = buf }) + + -- setup the window + -- local w = math.ceil(vim.o.columns * 1.0) + -- local h = math.ceil(vim.o.lines * 0.4) + local win = vim.api.nvim_open_win(buf, true, { + -- style = 'minimal', + -- relative = 'editor', + -- width = w, + -- height = h, + -- row = math.ceil(vim.o.lines - h), + -- col = vim.o.columns, + -- border = { ' ', '─', ' ', ' ', ' ', '─', ' ', ' ' }, + split = 'below', + win = -1, + }) + vim.api.nvim_set_current_win(win) + + -- spawn the command + if #opts.cmd == 0 then + opts.cmd = { vim.env.SHELL } + end + + -- vim.notify('termopen: running ' .. vim.inspect(opts.cmd), vim.log.levels.DEBUG) + + jobs.start(vim.tbl_deep_extend('error', opts, { + term = true, + env = { + -- we need this to avoid opening in the term buffer + NVR_PRE_CMD = 'close', + }, + on_exit = function() + -- if vim.api.nvim_win_is_valid(win) then + -- vim.api.nvim_win_close(win, true) + -- end + end, + })) + + -- put into insert mode + vim.cmd.startinsert() +end + +-- wrappers convenience + +function M.term(a) + local cwd = nil + local args = a.fargs + + if #args > 0 then + if args[1]:sub(0, 4) == 'cwd=' then + cwd = args[1]:sub(5) + args = vim.list_slice(args, 2) + end + end + + for i = 0, #args do + if args[i] == '%' then + args[i] = vim.fn.expand(args[i]) + end + end + + M.termopen({ cmd = args, cwd = cwd }) +end + +function M.make(a) + if vim.opt_local.makeprg == nil then + vim.ui.input({ prompt = 'Enter makeprg: ' }, function(input) + vim.opt_local.makeprg = input + end) + end + + local makeprg = vim.opt_local.makeprg:get() + if string.match(makeprg, [[%$%*]]) == nil then + makeprg = makeprg .. ' ' .. a.args + end + + local cmd = vim.split(makeprg, ' ') + cmd = vim.tbl_map(function(p) + p, _ = string.gsub(p, [[%$%*]], a.args) + p, _ = vim.fn.expand(p) + return p + end, cmd) + + M.termopen({ cmd = cmd }) +end + +return M diff --git a/.config/nvim/lua/mod/utils.lua b/.config/nvim/lua/mod/utils.lua new file mode 100644 index 0000000..1f3cd8b --- /dev/null +++ b/.config/nvim/lua/mod/utils.lua @@ -0,0 +1,15 @@ +local M = {} + +function M.buf_edit_lines(buf, start_row, end_row, callback) + local lines = {} + lines = vim.api.nvim_buf_get_lines(buf, start_row, end_row, true) + lines = callback(lines) + return vim.api.nvim_buf_set_lines(buf, start_row, end_row, true, lines) +end + +function M.edit_current_line(callback) + local row, _ = unpack(vim.api.nvim_win_get_cursor(0)) + return M.buf_edit_lines(0, row - 1, row, callback) +end + +return M diff --git a/.config/nvim/lua/mod/win.lua b/.config/nvim/lua/mod/win.lua new file mode 100644 index 0000000..e5c5f71 --- /dev/null +++ b/.config/nvim/lua/mod/win.lua @@ -0,0 +1,110 @@ +local M = {} + +function M.open(name, content, opts) + if name == '' then + return + end + if #content == 0 then + return + end + + local opts = opts or {} + local autoclose = opts.autoclose or false + local buf_opts = opts.buf or { buftype = '' } + + local win_opts = opts.win or false + -- profiles + if type(win_opts) == 'table' and type(win_opts[1] == 'string') then + local profile = win_opts[1] + local overwrites = unpack(win_opts, 2) + if profile == 'emplace' then + win_opts = false + elseif profile == 'smallsplit' then + win_opts = { + split = 'above', + win = 0, + height = 20, + } + elseif profile == 'popup' then + local winh = vim.api.nvim_win_get_height(0) + local winw = vim.api.nvim_win_get_width(0) + local winsize = 0.5 + win_opts = { + relative = 'win', + height = math.floor(winh * winsize), + width = math.floor(winw * winsize), + row = math.floor((winh * 0.5) - (0.5 * (winh * winsize))), + col = math.floor((winw * 0.5) - (0.5 * (winw * winsize))), + style = 'minimal', + border = 'single', + zindex = 99, + title = name, + } + end + if overwrites then + for k, v in pairs(overwrites) do + win_opts[k] = v + end + end + end + + local bufnr = vim.api.nvim_create_buf(false, true) + assert(bufnr ~= 0, 'Failed to create buffer') + + vim.bo[bufnr].modifiable = true + vim.api.nvim_buf_set_name(bufnr, name) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, content) + for k, v in pairs(buf_opts) do + vim.bo[bufnr][k] = v + end + + -- without win_opts (by default) replace current buffer + local winid = 0 + if win_opts then + winid = vim.api.nvim_open_win(0, not autoclose, win_opts) + assert(winid ~= 0, 'Failed to create window') + end + + vim.keymap.set('n', 'q', '<cmd>quit!<cr>', { silent = true, buffer = bufnr }) + + if autoclose then + local group = 'modwin_' .. vim.base64.encode(vim.uv.random(16)) + local group_id = vim.api.nvim_create_augroup(group, {}) + + vim.api.nvim_create_autocmd('WinClosed', { + pattern = tostring(winid), + group = group_id, + callback = function() + vim.api.nvim_create_augroup(group, {}) + M.winid = nil + end, + }) + vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, { + group = group_id, + callback = function() + vim.api.nvim_create_augroup(group, {}) + pcall(vim.api.nvim_win_close, winid, true) + end, + }) + end + + vim.api.nvim_win_set_buf(winid, bufnr) + return { bufnr, winid } +end + +function M.popup(name, content, opts) + local defaults = { + autoclose = true, + buf = { + bufhidden = 'wipe', + buftype = 'nofile', + modifiable = false, + }, + win = { 'popup' }, + } + opts = opts or {} + opts = vim.tbl_deep_extend('force', defaults, opts) + M.open(name, content, opts) +end + +return M |