Make Popups Configurable (#129)

This makes the popups in the plugin (those for editing and creating comments, replies, the pipeline, etc) configurable. Users can change the default width, height, transparency, and border properties, and set overrides per popup.
This commit is contained in:
Jakub F. Bortlík
2023-12-05 14:06:32 +01:00
committed by GitHub
parent 02db3e4b0e
commit cc68476b15
8 changed files with 75 additions and 37 deletions

View File

@@ -20,7 +20,7 @@ If you are using Lazy as a plugin manager, the easiest way to work on changes is
build = function() build = function()
require("gitlab.server").build() require("gitlab.server").build()
end, end,
dir = "~/.path/to/your-closed-version", -- Pass in the path to your cloned repository dir = "~/.path/to/your-cloned-version", -- Pass in the path to your cloned repository
config = function() config = function()
require("gitlab").setup({}) require("gitlab").setup({})
end, end,
@@ -31,18 +31,18 @@ If you are making changes to the Go codebase, don't forget to run `make compile`
3. Apply formatters and linters to your changes 3. Apply formatters and linters to your changes
For changes to the Go codbase: We use <a href="https://pkg.go.dev/cmd/gofmt">gofmt</a> to check formatting and <a href="https://github.com/golangci/golangci-lint">golangci-lint</a> to check linting. Run these commands in the root of the repository: For changes to the Go codebase: We use <a href="https://pkg.go.dev/cmd/gofmt">gofmt</a> to check formatting and <a href="https://github.com/golangci/golangci-lint">golangci-lint</a> to check linting. Run these commands in the root of the repository:
```bash ```bash
$ stylua . $ go fmt ./...
$ luacheck --globals vim busted --no-max-line-length -- . $ golangci-lint run
``` ```
For changes to the Lua codebase: We use <a href="https://github.com/JohnnyMorganz/StyLua">stylua</a> for formatting and <a href="https://github.com/mpeterv/luacheck">luacheck</a> for linting. Run these commands in the root of the repository: For changes to the Lua codebase: We use <a href="https://github.com/JohnnyMorganz/StyLua">stylua</a> for formatting and <a href="https://github.com/mpeterv/luacheck">luacheck</a> for linting. Run these commands in the root of the repository:
```bash ```bash
$ go fmt ./... $ stylua .
$ golangci-lint run $ luacheck --globals vim busted --no-max-line-length -- .
``` ```
4. Make the merge request to the `main` branch of `.gitlab.nvim` 4. Make the merge request to the `main` branch of `.gitlab.nvim`

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ bin
tests/plugins tests/plugins
!tests/plugins/.placeholder !tests/plugins/.placeholder
luacov.* luacov.*
tags

View File

@@ -76,7 +76,8 @@ use {
"MunifTanjim/nui.nvim", "MunifTanjim/nui.nvim",
"nvim-lua/plenary.nvim", "nvim-lua/plenary.nvim",
"sindrets/diffview.nvim", "sindrets/diffview.nvim",
"stevearc/dressing.nvim", "stevearc/dressing.nvim", -- Recommended but not required. Better UI for pickers.
"nvim-tree/nvim-web-devicons", -- Recommended but not required. Icons in discussion tree.
}, },
run = function() require("gitlab.server").build(true) end, run = function() require("gitlab.server").build(true) end,
config = function() config = function()
@@ -120,6 +121,15 @@ require("gitlab").setup({
exit = "<Esc>", exit = "<Esc>",
perform_action = "<leader>s", -- Once in normal mode, does action (like saving comment or editing description, etc) perform_action = "<leader>s", -- Once in normal mode, does action (like saving comment or editing description, etc)
perform_linewise_action = "<leader>l", -- Once in normal mode, does the linewise action (see logs for this job, etc) perform_linewise_action = "<leader>l", -- Once in normal mode, does the linewise action (see logs for this job, etc)
width = "40%",
height = "60%",
border = "rounded", -- One of "rounded", "single", "double", "solid"
opacity = 1.0, -- From 0.0 (fully transparent) to 1.0 (fully opaque)
comment = nil, -- Individual popup overrides, e.g. { width = "60%", height = "80%", border = "single", opacity = 0.85 },
edit = nil,
note = nil,
pipeline = nil,
reply = nil,
}, },
discussion_tree = { -- The discussion tree that holds all comments discussion_tree = { -- The discussion tree that holds all comments
blacklist = {}, -- List of usernames to remove from tree (bots, CI, etc) blacklist = {}, -- List of usernames to remove from tree (bots, CI, etc)
@@ -252,7 +262,7 @@ To display all discussions for the current MR, use the `toggle_discussions` acti
require("gitlab").toggle_discussions() require("gitlab").toggle_discussions()
``` ```
You can jump to the comment's location in the reviewer window by using the `state.settings.discussion_tree.jump_to_reviewer` key, or the actual file with the 'state.settings.discussion_tree.jump_to_file' key. You can jump to the comment's location in the reviewer window by using the `state.settings.discussion_tree.jump_to_reviewer` key, or to the actual file with the `state.settings.discussion_tree.jump_to_file` key.
Within the discussion tree, you can delete/edit/reply to comments with the `state.settings.discussion_tree.SOME_ACTION` keybindings. Within the discussion tree, you can delete/edit/reply to comments with the `state.settings.discussion_tree.SOME_ACTION` keybindings.
@@ -270,20 +280,20 @@ By default when reviewing files you will see signs and diagnostics ( if enabled
require("gitlab").move_to_discussion_tree_from_diagnostic() require("gitlab").move_to_discussion_tree_from_diagnostic()
``` ```
The `discussion_sign` configuration controls the display of signs for discussions in the reviewer pane. Keep in mind that the highlights provided here can be overridden by other highlights (for example from diffview.nvim). This allows users to jump to comments in the current buffer in the reviewer pane directly. The `discussion_sign` configuration controls the display of signs for discussions in the reviewer pane. This allows users to jump to comments in the current buffer in the reviewer pane directly. Keep in mind that the highlights provided here can be overridden by other highlights (for example from `diffview.nvim`).
These diagnostics are configurable in the same way that diagnostics are typically configurable in Neovim. For instance, the `severity` key sets the diagnostic severity level and should be set to one of `vim.diagnostic.severity.ERROR`, `vim.diagnostic.severity.WARN`, `vim.diagnostic.severity.INFO`, or `vim.diagnostic.severity.HINT`. The `display_opts` option configures the diagnostic display options where you can configure values like (this is directly used as opts in vim.diagnostic.set): These diagnostics are configurable in the same way that diagnostics are typically configurable in Neovim. For instance, the `severity` key sets the diagnostic severity level and should be set to one of `vim.diagnostic.severity.ERROR`, `vim.diagnostic.severity.WARN`, `vim.diagnostic.severity.INFO`, or `vim.diagnostic.severity.HINT`. The `display_opts` option configures the diagnostic display options (this is directly used as opts in vim.diagnostic.set). Here you can configure values like:
- `virtual_text` - Show virtual text for diagnostics. - `virtual_text` - Show virtual text for diagnostics.
- `underline` - Underline text for diagnostics. - `underline` - Underline text for diagnostics.
Diagnostics for discussions use the `gitlab_discussion` namespace. See `:h vim.diagnostic.config` and `:h diagnostic-structure` for more details. Signs and diagnostics have common settings in `discussion_sign_and_diagnostics`. This allows customizing if discussions that are resolved or no longer relevant should still display visual indicators in the editor. The `skip_resolved_discussion` Boolean will control visibility of resolved discussions, and `skip_old_revision_discussion` whether to show signs and diagnostics for discussions on outdated diff revisions. Diagnostics for discussions use the `gitlab_discussion` namespace. See `:h vim.diagnostic.config` and `:h diagnostic-structure` for more details. Signs and diagnostics have common settings in `discussion_sign_and_diagnostic`. This allows customizing if discussions that are resolved or no longer relevant should still display visual indicators in the editor. The `skip_resolved_discussion` Boolean will control visibility of resolved discussions, and `skip_old_revision_discussion` whether to show signs and diagnostics for discussions on outdated diff revisions.
When interacting with multiline comments, the cursor must be on the "main" line of diagnostic, where the `discussion_sign.text` is shown, otherwise `vim.diagnostic.show` and `jump_to_discussion_tree_from_diagnostic` will not work. When interacting with multiline comments, the cursor must be on the "main" line of diagnostic, where the `discussion_sign.text` is shown, otherwise `vim.diagnostic.show` and `jump_to_discussion_tree_from_diagnostic` will not work.
### Uploading Files ### Uploading Files
To attach a file to an MR description, reply, comment, and so forth use the `settings.popup.perform_linewise_action` keybinding when the popup is open. This will open a picker that will look in the directory you specify in the `settings.attachment_dir` folder (this must be an absolute path) for files. To attach a file to an MR description, reply, comment, and so forth use the `settings.popup.perform_linewise_action` keybinding when the popup is open. This will open a picker that will look for files in the directory you specify in the `settings.attachment_dir` folder (this must be an absolute path).
When you have picked the file, it will be added to the current buffer at the current line. When you have picked the file, it will be added to the current buffer at the current line.
@@ -331,8 +341,7 @@ require("dressing").setup({
### Restarting or Shutting Down ### Restarting or Shutting Down
The `gitlab.nvim` server will shut down automatically when you exit Neovim. However, if you would like to manage this yourself (for instance, restart the server when you check out a new branch) you may do so via the `restart` command, or `shutdown` commands, which The `gitlab.nvim` server will shut down automatically when you exit Neovim. However, if you would like to manage this yourself (for instance, restart the server when you check out a new branch) you may do so via the `restart` command, or `shutdown` commands, which both accept callbacks.
both accept callbacks.
```lua ```lua
require("gitlab.server").restart() require("gitlab.server").restart()
@@ -382,7 +391,7 @@ vim.keymap.set("n", "glo", gitlab.open_in_browser)
**To check that the current settings of the plugin are configured correctly, please run: `:lua require("gitlab").print_settings()`** **To check that the current settings of the plugin are configured correctly, please run: `:lua require("gitlab").print_settings()`**
This plugin uses a Golang server to reach out to Gitlab. It's possible that something is going wrong when starting that server or connecting with Gitlab. The Golang server runs outside of Neovim, and can be interacted with directly in order to troubleshoot. To start the server, check out your feature branch and run these commands: This plugin uses a Go server to reach out to Gitlab. It's possible that something is going wrong when starting that server or connecting with Gitlab. The Go server runs outside of Neovim, and can be interacted with directly in order to troubleshoot. To start the server, check out your feature branch and run these commands:
```lua ```lua
:lua require("gitlab.server").build(true) :lua require("gitlab.server").build(true)

View File

@@ -10,12 +10,17 @@ local miscellaneous = require("gitlab.actions.miscellaneous")
local reviewer = require("gitlab.reviewer") local reviewer = require("gitlab.reviewer")
local M = {} local M = {}
local comment_popup = Popup(u.create_popup_state("Comment", "40%", "60%")) -- Popup creation is wrapped in a function so that it is performed *after* user
local note_popup = Popup(u.create_popup_state("Note", "40%", "60%")) -- configuration has been merged with default configuration, not when this file is being
-- required.
local function create_comment_popup()
return Popup(u.create_popup_state("Comment", state.settings.popup.comment))
end
-- This function will open a comment popup in order to create a comment on the changed/updated -- This function will open a comment popup in order to create a comment on the changed/updated
-- line in the current MR -- line in the current MR
M.create_comment = function() M.create_comment = function()
local comment_popup = create_comment_popup()
comment_popup:mount() comment_popup:mount()
state.set_popup_keymaps(comment_popup, function(text) state.set_popup_keymaps(comment_popup, function(text)
M.confirm_create_comment(text) M.confirm_create_comment(text)
@@ -27,6 +32,7 @@ M.create_multiline_comment = function()
if not u.check_visual_mode() then if not u.check_visual_mode() then
return return
end end
local comment_popup = create_comment_popup()
local start_line, end_line = u.get_visual_selection_boundaries() local start_line, end_line = u.get_visual_selection_boundaries()
comment_popup:mount() comment_popup:mount()
state.set_popup_keymaps(comment_popup, function(text) state.set_popup_keymaps(comment_popup, function(text)
@@ -40,6 +46,7 @@ M.create_comment_suggestion = function()
if not u.check_visual_mode() then if not u.check_visual_mode() then
return return
end end
local comment_popup = create_comment_popup()
local start_line, end_line = u.get_visual_selection_boundaries() local start_line, end_line = u.get_visual_selection_boundaries()
local current_line = vim.api.nvim_win_get_cursor(0)[1] local current_line = vim.api.nvim_win_get_cursor(0)[1]
local range = end_line - start_line local range = end_line - start_line
@@ -81,6 +88,7 @@ M.create_comment_suggestion = function()
end end
M.create_note = function() M.create_note = function()
local note_popup = Popup(u.create_popup_state("Note", state.settings.popup.note))
note_popup:mount() note_popup:mount()
state.set_popup_keymaps(note_popup, function(text) state.set_popup_keymaps(note_popup, function(text)
M.confirm_create_comment(text, nil, true) M.confirm_create_comment(text, nil, true)

View File

@@ -13,8 +13,6 @@ local reviewer = require("gitlab.reviewer")
local miscellaneous = require("gitlab.actions.miscellaneous") local miscellaneous = require("gitlab.actions.miscellaneous")
local discussions_tree = require("gitlab.actions.discussions.tree") local discussions_tree = require("gitlab.actions.discussions.tree")
local edit_popup = Popup(u.create_popup_state("Edit Comment", "80%", "80%"))
local reply_popup = Popup(u.create_popup_state("Reply", "80%", "80%"))
local discussion_sign_name = "gitlab_discussion" local discussion_sign_name = "gitlab_discussion"
local discussion_helper_sign_start = "gitlab_discussion_helper_start" local discussion_helper_sign_start = "gitlab_discussion_helper_start"
local discussion_helper_sign_mid = "gitlab_discussion_helper_mid" local discussion_helper_sign_mid = "gitlab_discussion_helper_mid"
@@ -470,6 +468,7 @@ end
-- The reply popup will mount in a window when you trigger it (settings.discussion_tree.reply) when hovering over a node in the discussion tree. -- The reply popup will mount in a window when you trigger it (settings.discussion_tree.reply) when hovering over a node in the discussion tree.
M.reply = function(tree) M.reply = function(tree)
local reply_popup = Popup(u.create_popup_state("Reply", state.settings.popup.reply))
local node = tree:get_node() local node = tree:get_node()
local discussion_node = M.get_root_node(tree, node) local discussion_node = M.get_root_node(tree, node)
local id = tostring(discussion_node.id) local id = tostring(discussion_node.id)
@@ -536,6 +535,7 @@ end
-- This function (settings.discussion_tree.edit_comment) will open the edit popup for the current comment in the discussion tree -- This function (settings.discussion_tree.edit_comment) will open the edit popup for the current comment in the discussion tree
M.edit_comment = function(tree, unlinked) M.edit_comment = function(tree, unlinked)
local edit_popup = Popup(u.create_popup_state("Edit Comment", state.settings.popup.edit))
local current_node = tree:get_node() local current_node = tree:get_node()
local note_node = M.get_note_node(tree, current_node) local note_node = M.get_note_node(tree, current_node)
local root_node = M.get_root_node(tree, current_node) local root_node = M.get_root_node(tree, current_node)
@@ -811,37 +811,37 @@ M.set_tree_keymaps = function(tree, bufnr, unlinked)
if M.is_current_node_note(tree) then if M.is_current_node_note(tree) then
M.edit_comment(tree, unlinked) M.edit_comment(tree, unlinked)
end end
end, { buffer = bufnr }) end, { buffer = bufnr, desc = "Edit comment" })
vim.keymap.set("n", state.settings.discussion_tree.delete_comment, function() vim.keymap.set("n", state.settings.discussion_tree.delete_comment, function()
if M.is_current_node_note(tree) then if M.is_current_node_note(tree) then
M.delete_comment(tree, unlinked) M.delete_comment(tree, unlinked)
end end
end, { buffer = bufnr }) end, { buffer = bufnr, desc = "Delete comment" })
vim.keymap.set("n", state.settings.discussion_tree.toggle_resolved, function() vim.keymap.set("n", state.settings.discussion_tree.toggle_resolved, function()
if M.is_current_node_note(tree) then if M.is_current_node_note(tree) then
M.toggle_discussion_resolved(tree) M.toggle_discussion_resolved(tree)
end end
end, { buffer = bufnr }) end, { buffer = bufnr, desc = "Toggle resolved" })
vim.keymap.set("n", state.settings.discussion_tree.toggle_node, function() vim.keymap.set("n", state.settings.discussion_tree.toggle_node, function()
M.toggle_node(tree) M.toggle_node(tree)
end, { buffer = bufnr }) end, { buffer = bufnr, desc = "Toggle node" })
vim.keymap.set("n", state.settings.discussion_tree.reply, function() vim.keymap.set("n", state.settings.discussion_tree.reply, function()
if M.is_current_node_note(tree) then if M.is_current_node_note(tree) then
M.reply(tree) M.reply(tree)
end end
end, { buffer = bufnr }) end, { buffer = bufnr, desc = "Reply" })
if not unlinked then if not unlinked then
vim.keymap.set("n", state.settings.discussion_tree.jump_to_file, function() vim.keymap.set("n", state.settings.discussion_tree.jump_to_file, function()
if M.is_current_node_note(tree) then if M.is_current_node_note(tree) then
M.jump_to_file(tree) M.jump_to_file(tree)
end end
end, { buffer = bufnr }) end, { buffer = bufnr, desc = "Jump to file" })
vim.keymap.set("n", state.settings.discussion_tree.jump_to_reviewer, function() vim.keymap.set("n", state.settings.discussion_tree.jump_to_reviewer, function()
if M.is_current_node_note(tree) then if M.is_current_node_note(tree) then
M.jump_to_reviewer(tree) M.jump_to_reviewer(tree)
end end
end, { buffer = bufnr }) end, { buffer = bufnr, desc = "Jump to reviewer" })
end end
end end

View File

@@ -1,4 +1,4 @@
-- This module is responsible for the MR pipline -- This module is responsible for the MR pipeline
-- This lets the user see the current status of the pipeline -- This lets the user see the current status of the pipeline
-- and retrigger the pipeline from within the editor -- and retrigger the pipeline from within the editor
local Popup = require("nui.popup") local Popup = require("nui.popup")
@@ -42,7 +42,8 @@ M.open = function()
local width = string.len(pipeline.web_url) + 10 local width = string.len(pipeline.web_url) + 10
local height = 6 + #pipeline_jobs + 3 local height = 6 + #pipeline_jobs + 3
local pipeline_popup = Popup(u.create_popup_state("Loading Pipeline...", width, height)) local pipeline_popup =
Popup(u.create_popup_state("Loading Pipeline...", state.settings.popup.pipeline, width, height))
M.pipeline_popup = pipeline_popup M.pipeline_popup = pipeline_popup
pipeline_popup:mount() pipeline_popup:mount()

View File

@@ -18,6 +18,15 @@ M.settings = {
exit = "<Esc>", exit = "<Esc>",
perform_action = "<leader>s", perform_action = "<leader>s",
perform_linewise_action = "<leader>l", perform_linewise_action = "<leader>l",
width = "40%",
height = "60%",
border = "rounded",
opacity = 1.0,
edit = nil,
reply = nil,
comment = nil,
note = nil,
pipeline = nil,
}, },
discussion_tree = { discussion_tree = {
blacklist = {}, blacklist = {},
@@ -207,7 +216,7 @@ M.set_popup_keymaps = function(popup, action, linewise_action, opts)
end end
vim.keymap.set("n", M.settings.popup.exit, function() vim.keymap.set("n", M.settings.popup.exit, function()
exit(popup, opts.cb) exit(popup, opts.cb)
end, { buffer = popup.bufnr }) end, { buffer = popup.bufnr, desc = "Exit popup" })
if action ~= nil then if action ~= nil then
vim.keymap.set("n", M.settings.popup.perform_action, function() vim.keymap.set("n", M.settings.popup.perform_action, function()
local text = u.get_buffer_text(popup.bufnr) local text = u.get_buffer_text(popup.bufnr)
@@ -218,7 +227,7 @@ M.set_popup_keymaps = function(popup, action, linewise_action, opts)
exit(popup) exit(popup)
action(text, popup.bufnr) action(text, popup.bufnr)
end end
end, { buffer = popup.bufnr }) end, { buffer = popup.bufnr, desc = "Perform action" })
end end
if linewise_action ~= nil then if linewise_action ~= nil then
@@ -227,7 +236,7 @@ M.set_popup_keymaps = function(popup, action, linewise_action, opts)
local linnr = vim.api.nvim_win_get_cursor(0)[1] local linnr = vim.api.nvim_win_get_cursor(0)[1]
local text = u.get_line_content(bufnr, linnr) local text = u.get_line_content(bufnr, linnr)
linewise_action(text) linewise_action(text)
end, { buffer = popup.bufnr }) end, { buffer = popup.bufnr, desc = "Perform linewise action" })
end end
end end

View File

@@ -344,8 +344,16 @@ M.jump_to_buffer = function(bufnr, line_number)
vim.api.nvim_win_set_cursor(0, { line_number, 0 }) vim.api.nvim_win_set_cursor(0, { line_number, 0 })
end end
M.create_popup_state = function(title, width, height) ---Get the popup view_opts
return { ---@param title string The string to appear on top of the popup
---@param settings table User defined popup settings
---@param width string Override default width
---@param height string Override default height
---@return table
M.create_popup_state = function(title, settings, width, height)
local default_settings = require("gitlab.state").settings.popup
local user_settings = settings or {}
local view_opts = {
buf_options = { buf_options = {
filetype = "markdown", filetype = "markdown",
}, },
@@ -353,17 +361,19 @@ M.create_popup_state = function(title, width, height)
enter = true, enter = true,
focusable = true, focusable = true,
border = { border = {
style = "rounded", style = user_settings.border or default_settings.border,
text = { text = {
top = title, top = title,
}, },
}, },
position = "50%", position = "50%",
size = { size = {
width = width, width = user_settings.width or width or default_settings.width,
height = height, height = user_settings.height or height or default_settings.height,
}, },
opacity = user_settings.opacity or default_settings.opacity,
} }
return view_opts
end end
M.read_file = function(file_path) M.read_file = function(file_path)