From 597e2f84959a6fb18784200d612bf1a25620734d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mun=CC=83oz?= Date: Mon, 1 Mar 2021 23:56:06 +0100 Subject: [PATCH] use float window for adding comments --- lua/octo/commands.lua | 28 ++----- lua/octo/menu.lua | 18 ++--- lua/octo/reviews.lua | 105 ++++++------------------ lua/octo/util.lua | 184 +++++++++++++++++++++++++++++++----------- 4 files changed, 176 insertions(+), 159 deletions(-) diff --git a/lua/octo/commands.lua b/lua/octo/commands.lua index eb12e60a..874d5019 100644 --- a/lua/octo/commands.lua +++ b/lua/octo/commands.lua @@ -685,7 +685,7 @@ function M.pr_checks() end table.insert(lines, table.concat(line, " ")) end - local _, bufnr = util.create_content_popup(lines) + local _, bufnr = util.create_popup({content=lines}) local buf_lines = api.nvim_buf_get_lines(bufnr, 0, -1, false) for i, l in ipairs(buf_lines) do if #vim.split(l, "pass") > 1 then @@ -789,29 +789,17 @@ function M.review_threads() end function M.submit_review() - local winnr, bufnr = - util.create_popup( - {""}, - { - line = 5, - col = 5, - width = math.floor(vim.o.columns * 0.9), - height = math.floor(vim.o.lines * 0.5) - } - ) - api.nvim_set_current_win(winnr) + local winid, bufnr = util.create_popup({ + header = "Press to approve, to comment or to request changes" + }) + api.nvim_set_current_win(winid) api.nvim_buf_set_option(bufnr, "syntax", "markdown") - local help_vt = { - {"Press to approve, to comment or to request changes", "OctoNvimDetailsValue"} - } - writers.write_block({"", "", ""}, {bufnr = bufnr, mark = false, line = 1}) - writers.write_virtual_text(bufnr, constants.OCTO_TITLE_VT_NS, 0, help_vt) local mapping_opts = {script = true, silent = true, noremap = true} api.nvim_buf_set_keymap(bufnr, "i", "", "", mapping_opts) - api.nvim_buf_set_keymap(bufnr, "n", "q", format(":call nvim_win_close(%d, 1)", winnr), mapping_opts) - api.nvim_buf_set_keymap(bufnr, "n", "", format(":call nvim_win_close(%d, 1)", winnr), mapping_opts) - api.nvim_buf_set_keymap(bufnr, "n", "", format(":call nvim_win_close(%d, 1)", winnr), mapping_opts) + api.nvim_buf_set_keymap(bufnr, "n", "q", format(":call nvim_win_close(%d, 1)", winid), mapping_opts) + api.nvim_buf_set_keymap(bufnr, "n", "", format(":call nvim_win_close(%d, 1)", winid), mapping_opts) + api.nvim_buf_set_keymap(bufnr, "n", "", format(":call nvim_win_close(%d, 1)", winid), mapping_opts) api.nvim_buf_set_keymap(bufnr, "n", "", ":lua require'octo.reviews'.submit_review('APPROVE')", mapping_opts) api.nvim_buf_set_keymap(bufnr, "n", "", ":lua require'octo.reviews'.submit_review('COMMENT')", mapping_opts) api.nvim_buf_set_keymap( diff --git a/lua/octo/menu.lua b/lua/octo/menu.lua index ef5f4da9..a584799a 100644 --- a/lua/octo/menu.lua +++ b/lua/octo/menu.lua @@ -569,16 +569,20 @@ end function M.review_comments() local comments = vim.tbl_values(reviews.review_comments) local max_linenr_length = -1 + local filtered_comments = {} for _, comment in ipairs(comments) do max_linenr_length = math.max(max_linenr_length, #tostring(comment.line1)) max_linenr_length = math.max(max_linenr_length, #tostring(comment.line2)) + if not util.is_blank(vim.fn.trim(comment.body)) then + table.insert(filtered_comments, comment) + end end pickers.new( {}, { prompt_prefix = "Review Comments >", finder = finders.new_table { - results = comments, + results = filtered_comments, entry_maker = entry_maker.gen_from_review_comment(max_linenr_length) }, sorter = conf.generic_sorter({}), @@ -607,18 +611,6 @@ function M.review_comments() if diff_winid > -1 then api.nvim_win_set_cursor(diff_winid, {comment.line1, 1}) end - - -- show comment win/buf - if comment.comment_winid and api.nvim_win_is_valid(comment.comment_winid) then - api.nvim_win_set_buf(comment.comment_winid, comment.comment_bufnr) - api.nvim_set_current_win(comment.comment_winid) - else - -- move to qf win - api.nvim_set_current_win(comment.qf_winid) - - -- create new win and show comment bufnr - vim.cmd(format("rightbelow vert sbuffer %d", comment.comment_bufnr)) - end end) return true end diff --git a/lua/octo/reviews.lua b/lua/octo/reviews.lua index a0f7d668..742c96f0 100644 --- a/lua/octo/reviews.lua +++ b/lua/octo/reviews.lua @@ -157,7 +157,7 @@ function M.diff_changes_qf_entry() api.nvim_win_set_buf(right_win, right_bufnr) M.add_changes_qf_mappings() vim.cmd(format("leftabove vert sbuffer %d", left_bufnr)) - local left_win = api.nvim_get_current_win() + local left_win = util.getwin4buf(left_bufnr) M.add_changes_qf_mappings() local write_diff_lines = function(lines, side) @@ -227,57 +227,28 @@ function M.add_review_comment(isSuggestion) return end - -- create new buffer - local bufname = format("%s:%d.%d", string.gsub(props.bufname, "/file/", "/comment/"), line1, line2) - local comment_bufnr - if vim.fn.bufnr(bufname) > -1 then - comment_bufnr = vim.fn.bufnr(bufname) - api.nvim_buf_set_lines(comment_bufnr, 0, -1, false, {}) - else - comment_bufnr = api.nvim_create_buf(false, true) - api.nvim_buf_set_option(comment_bufnr, "syntax", "markdown") - end - - -- check if there is a comment win already open - local _, comment_winid = pcall(api.nvim_win_get_var, props.qf_winid, "comment_winid") - if tonumber(comment_winid) and api.nvim_win_is_valid(comment_winid) then - -- move to comment win - api.nvim_win_set_buf(comment_winid, comment_bufnr) - api.nvim_set_current_win(comment_winid) - else - -- move to qf win - api.nvim_set_current_win(props.qf_winid) - - -- create new win and show comment bufnr - vim.cmd(format("rightbelow vert sbuffer %d", comment_bufnr)) - - -- store comment win id - comment_winid = api.nvim_get_current_win() - api.nvim_win_set_var(props.qf_winid, "comment_winid", comment_winid) - end + -- create comment window and buffer + local comment_winid, comment_bufnr = util.create_popup({ + header = format("Comment for %s (from %d to %d) [%s]", props.path, line1, line2, props.side) + }) + api.nvim_set_current_win(comment_winid) + api.nvim_buf_set_option(comment_bufnr, "syntax", "markdown") - -- add mappings to comment buffer - M.add_changes_qf_mappings() - - -- header - -- local header_vt = { - -- {format("%s", props.path), "OctoNvimDetailsValue"}, - -- {" ["}, - -- {format("%s", props.side), "OctoNvimDetailsLabel"}, - -- {"] ("}, - -- {format("%d,%d", line1, line2), "OctoNvimDetailsValue"}, - -- {")"} - -- } - -- writers.write_block({"", ""}, {bufnr = comment_bufnr, line = 1}) - -- writers.write_virtual_text(comment_bufnr, constants.OCTO_TITLE_VT_NS, 0, header_vt) + local bufname = format("%s:%d.%d", string.gsub(props.bufname, "/file/", "/comment/"), line1, line2) + api.nvim_buf_set_name(comment_bufnr, bufname) + api.nvim_buf_set_option(comment_bufnr, "filetype", "octo_reviewcomment") + api.nvim_buf_set_option(comment_bufnr, "syntax", "markdown") + api.nvim_buf_set_option(comment_bufnr, "buftype", "acwrite") + api.nvim_buf_set_option(comment_bufnr, "modified", false) + api.nvim_buf_set_var(comment_bufnr, "OctoDiffProps", props) + api.nvim_win_set_var(props.qf_winid, "comment_winid", comment_winid) if isSuggestion then local lines = api.nvim_buf_get_lines(props.content_bufnr, line1-1, line2, false) - writers.write_block({"```suggestion"}, {bufnr = comment_bufnr, line=1 }) - writers.write_block(lines, {bufnr = comment_bufnr }) - writers.write_block({"```"}, {bufnr = comment_bufnr }) - -- else - -- writers.write_block({""}, {bufnr = comment_bufnr }) + local suggestion = {"```suggestion"} + vim.list_extend(suggestion, lines) + table.insert(suggestion, "```") + api.nvim_buf_set_lines(comment_bufnr, 0, -1, false, suggestion) end -- change to insert mode @@ -303,14 +274,6 @@ function M.add_review_comment(isSuggestion) -- add comment to list of pending comments M.review_comments[bufname] = comment - - -- configure comment buffer - api.nvim_buf_set_var(comment_bufnr, "OctoDiffProps", props) - api.nvim_buf_set_option(comment_bufnr, "filetype", "octo_reviewcomment") - api.nvim_buf_set_option(comment_bufnr, "syntax", "markdown") - api.nvim_buf_set_option(comment_bufnr, "buftype", "acwrite") - api.nvim_buf_set_name(comment_bufnr, bufname) - api.nvim_buf_set_option(comment_bufnr, "modified", false) end end @@ -322,15 +285,11 @@ function M.save_review_comment() local comment = M.review_comments[bufname] local body = table.concat(api.nvim_buf_get_lines(bufnr, 0, -1, false), "\n") comment.body = vim.fn.trim(body) - if util.is_blank(comment.body) then - -- ignore empty comments - return - end M.review_comments[bufname] = comment - api.nvim_buf_set_option(bufnr, "modified", false) - - -- highlight commented lines - M.highlight_lines(comment.content_bufnr, comment.line1, comment.line2) + if not util.is_blank(comment.body) then + -- highlight commented lines + M.highlight_lines(comment.content_bufnr, comment.line1, comment.line2) + end end end @@ -430,7 +389,7 @@ function M.populate_reviewthreads_qf(repo, number, reviewthreads) -- open qf vim.cmd(format("%dcopen", qf_height)) - local qf_win = api.nvim_get_current_win() + local qf_win = vim.fn.getqflist({winid = 0}).winid -- highlight qf entries vim.cmd [[call matchadd("Comment", "\(.*\)")]] @@ -669,20 +628,6 @@ end function M.close_review_tab() vim.cmd [[silent! tabclose]] - - -- close fugitive buffers - --M.clean_fugitive_buffers() - - -- close review comment buffers - local tabpage = api.nvim_get_current_tabpage() - for _, w in ipairs(api.nvim_tabpage_list_wins(tabpage)) do - if api.nvim_win_is_valid(w) then - local bufnr = api.nvim_win_get_buf(w) - if api.nvim_buf_get_option(bufnr, "filetype") == "octo_reviewcomment" then - vim.cmd(format("bdelete! %d", bufnr)) - end - end - end end function M.add_changes_qf_mappings(bufnr) @@ -728,6 +673,7 @@ function M.submit_review(event) local comments = {} for _, c in ipairs(vim.tbl_values(M.review_comments)) do + if util.is_blank(vim.fn.trim(c.body)) then goto continue end if c.line1 == c.line2 then table.insert( comments, @@ -747,6 +693,7 @@ function M.submit_review(event) ) ) end + ::continue:: end comments = table.concat(comments, ", ") diff --git a/lua/octo/util.lua b/lua/octo/util.lua index 21ded37e..988b3638 100644 --- a/lua/octo/util.lua +++ b/lua/octo/util.lua @@ -8,7 +8,7 @@ local format = string.format local vim = vim local api = vim.api local json = { - parse = vim.fn.json_decode, + parse = vim.fn.json_decode } local M = {} @@ -187,8 +187,7 @@ function M.update_issue_metadata(bufnr) -- description has been removed -- the space in ' ' is crucial to prevent this block of code from repeating on TextChanged(I)? api.nvim_buf_set_lines(bufnr, start_line, start_line + 1, false, {" ", ""}) - local winnr = api.nvim_get_current_win() - api.nvim_win_set_cursor(winnr, {start_line + 1, 0}) + api.nvim_win_set_cursor(0, {start_line + 1, 0}) end M.update_metadata(metadata, start_line, end_line, text) api.nvim_buf_set_var(bufnr, "description", metadata) @@ -205,8 +204,7 @@ function M.update_issue_metadata(bufnr) -- comment has been removed -- the space in ' ' is crucial to prevent this block of code from repeating on TextChanged(I)? api.nvim_buf_set_lines(bufnr, start_line, start_line + 1, false, {" ", ""}) - local winnr = api.nvim_get_current_win() - api.nvim_win_set_cursor(winnr, {start_line + 1, 0}) + api.nvim_win_set_cursor(0, {start_line + 1, 0}) end M.update_metadata(metadata, start_line, end_line, text) @@ -234,7 +232,9 @@ function M.get_thread_at_cursor(bufnr, cursor) local marks = api.nvim_buf_get_extmarks(bufnr, constants.OCTO_THREAD_NS, 0, -1, {details = true}) for _, mark in ipairs(marks) do local info = thread_map[tostring(mark[1])] - if not info then goto continue end + if not info then + goto continue + end local thread_id = info.thread_id local first_comment_id = info.first_comment_id local start_line = mark[2] @@ -245,7 +245,7 @@ function M.get_thread_at_cursor(bufnr, cursor) ::continue:: end elseif vim.bo[bufnr].ft == "octo_reviewthread" then - local bufname = api.nvim_buf_get_name(bufnr) + local bufname = api.nvim_buf_get_name(bufnr) local thread_id, first_comment_id = string.match(bufname, "octo://.*/pull/%d+/reviewthread/(.*)/comment/(.*)") local end_line = api.nvim_buf_line_count(bufnr) - 1 return thread_id, 1, end_line, first_comment_id @@ -253,7 +253,6 @@ function M.get_thread_at_cursor(bufnr, cursor) return nil end - function M.update_reactions_at_cursor(bufnr, cursor, reaction_groups) local comments = api.nvim_buf_get_var(bufnr, "comments") for i, comment in ipairs(comments) do @@ -277,44 +276,117 @@ function M.format_date(date_string) return date(date_string):addminutes(time_bias):fmt(vim.g.octo_date_format) end -function M.create_content_popup(lines) +function M.create_popup(opts) + opts = opts or {} + opts.x_percent = opts.x_percent or 0.6 + opts.y_percent = opts.y_percent or 0.4 + + -- calculate vim height + local vim_height = vim.o.lines - vim.o.cmdheight + if vim.o.laststatus ~= 0 then + vim_height = vim_height - 1 + end + + -- calculate vim width + local vim_width = vim.o.columns + + -- calculate longest line in lines local max_line = -1 - for _, line in ipairs(lines) do - max_line = math.max(#line, max_line) + if opts.content then + for _, line in ipairs(opts.content) do + max_line = math.max(#line, max_line) + end + end + + -- calculate window height/width + local width, height + if max_line > 0 then + -- pre-defined content + width = math.min(vim_width * 0.9, max_line) + if opts.header then + height = math.min(vim_height, 2 + #opts.content) + else + height = math.min(vim_height, #opts.content) + end + else + width = math.floor(vim_width * opts.x_percent) + height = math.floor(vim_height * opts.y_percent) end - local line_count = vim.o.lines - vim.o.cmdheight - local max_width = math.min(vim.o.columns * 0.9, max_line) - if vim.o.laststatus ~= 0 then - line_count = line_count - 1 - end - local winnr, bufnr = M.create_popup(lines, { - line = (line_count - #lines) / 2, - col = (vim.o.columns - max_width) / 2, - height = #lines - }) - return winnr, bufnr -end -function M.create_popup(content, opts) - local winnr, _ = + -- calculate offsets + local x_offset = (vim_width - width) / 2 + local y_offset = (vim_height - height) / 2 + local header_winid + local header_win_opts = {border = {}} + if opts.header then + header_winid, header_win_opts = + popup.create( + {opts.header}, + { + moved = "non-existant", + line =y_offset, + col = x_offset, + minwidth = width, + maxheight = 2, + border = {1, 1, 0, 1}, + borderchars = {"─", "│", "", "│", "┌", "┐", "┤", "├"}, + padding = {0, 0, 0, 0} + } + ) + vim.cmd("set eventignore+=WinClosed,WinLeave") + local header_bufnr = api.nvim_win_get_buf(header_winid) + api.nvim_buf_set_option(header_bufnr, "modifiable", false) + end + local winid = popup.create( - content, + opts.content or {}, { - line = opts.line, - col = opts.col, - minwidth = opts.width or 40, - minheight = opts.height or 20, + moved = "non-existant", + line = opts.header and y_offset+2 or y_offset, + col = x_offset, + minwidth = width, + minheight = height, border = {1, 1, 1, 1}, - borderchars = {"─", "│", "─", "│", "┌", "┐", "┘", "└"}, - padding = {1, 1, 1, 1} + borderchars = opts.header and {"─", "│", "─", "│", "├", "┤", "┘", "└"} or {"─", "│", "─", "│", "┌", "┐", "┘", "└"}, + padding = {0, 0, 0, 0} } ) - local bufnr = api.nvim_win_get_buf(winnr) + local bufnr = api.nvim_win_get_buf(winid) + if header_winid then + vim.cmd(string.format( + "autocmd BufLeave,BufDelete ++nested ++once :lua require('octo.util').try_close_wins(%d, %d, %d)", + bufnr, + header_winid, + header_win_opts.border.win_id, + winid)) + vim.cmd(string.format( + "autocmd WinClosed,WinLeave ++nested ++once :lua require('octo.util').try_close_wins(%d, %d, %d)", + bufnr, + header_winid, + header_win_opts.border.win_id, + winid)) + else + vim.cmd(string.format( + "autocmd BufLeave,BufDelete ++nested ++once :lua require('octo.util').try_close_wins(%d)", + bufnr, + winid)) + vim.cmd(string.format( + "autocmd WinClosed,WinLeave ++nested ++once :lua require('octo.util').try_close_wins(%d)", + bufnr, + winid)) + end local mapping_opts = {script = true, silent = true, noremap = true} - api.nvim_buf_set_keymap(bufnr, "n", "q", format(":call nvim_win_close(%d, 1)", winnr), mapping_opts) - api.nvim_buf_set_keymap(bufnr, "n", "", format(":call nvim_win_close(%d, 1)", winnr), mapping_opts) - api.nvim_buf_set_keymap(bufnr, "n", "", format(":call nvim_win_close(%d, 1)", winnr), mapping_opts) - return winnr, bufnr + api.nvim_buf_set_keymap(bufnr, "n", "q", format("lua require'octo.util'.try_close_wins(%d)", winid), mapping_opts) + api.nvim_buf_set_keymap(bufnr, "n", "", format("lua require'octo.util'.try_close_wins(%d)", winid), mapping_opts) + api.nvim_buf_set_keymap(bufnr, "n", "", format("lua require'octo.util'.try_close_wins(%d)", winid), mapping_opts) + + return winid, bufnr +end + +function M.try_close_wins(...) + for _, win_id in ipairs({...}) do + pcall(vim.api.nvim_win_close, win_id, true) + end end function M.get_buffer_kind(bufnr) @@ -413,7 +485,7 @@ function M.escape_chars(string) string, '["]', { - ['"'] = '\\"', + ['"'] = '\\"' } ) end @@ -422,7 +494,9 @@ function M.open_in_browser() local repo, number = M.get_repo_number() local bufname = vim.fn.bufname() local _, type = string.match(bufname, "octo://(.+)/(.+)/(%d+)") - if type == "pull" then type = "pr" end + if type == "pull" then + type = "pr" + end local cmd = format("gh %s view --web -R %s %d", type, repo, number) print(cmd) os.execute(cmd) @@ -431,8 +505,8 @@ end function M.open_url_at_cursor() local uri = vim.fn.matchstr(vim.fn.getline("."), "[a-z]*:\\/\\/[^ >,;()]*") print(uri) - if uri then - require"octo.commands".parse_url(uri) + if uri then + require "octo.commands".parse_url(uri) else api.nvim_err_writeln("No URI found in line.") end @@ -459,18 +533,34 @@ function M.get_file_contents(repo, commit, path, cb) end end } - ) + ) end function M.set_timeout(delay, callback, ...) local timer = vim.loop.new_timer() local args = {...} - vim.loop.timer_start(timer, delay, 0, function () - vim.loop.timer_stop(timer) - vim.loop.close(timer) - callback(unpack(args)) - end) + vim.loop.timer_start( + timer, + delay, + 0, + function() + vim.loop.timer_stop(timer) + vim.loop.close(timer) + callback(unpack(args)) + end + ) return timer end +function M.getwin4buf(bufnr) + local tabpage = api.nvim_get_current_tabpage() + local wins = api.nvim_tabpage_list_wins(tabpage) + for _, w in ipairs(wins) do + if bufnr == api.nvim_win_get_buf(w) then + return w + end + end + return -1 +end + return M