Files
gitlab.nvim/lua/gitlab/state.lua
Harrison (Harry) Cramer 4f1fe4ae25 Allow insecure connections to Gitlab (#234)
This MR makes it possible to ignore bad x509 certificates when connecting to Gitlab, by creating a custom HTTP connection. The option is exposed via the setup function. This is a PATCH release.
2024-04-02 21:53:25 -04:00

337 lines
9.5 KiB
Lua

-- This module is responsible for holding and setting shared state between
-- modules, such as keybinding data and other settings and configuration.
-- This module is also responsible for ensuring that the state of the plugin
-- is valid via dependencies
local u = require("gitlab.utils")
local M = {}
M.emoji_map = nil
-- These are the default settings for the plugin
M.settings = {
port = nil, -- choose random port
debug = { go_request = false, go_response = false },
log_path = (vim.fn.stdpath("cache") .. "/gitlab.nvim.log"),
config_path = nil,
reviewer = "diffview",
reviewer_settings = {
diffview = {
imply_local = false,
},
},
connection_settings = {
insecure = true,
},
attachment_dir = "",
help = "g?",
popup = {
exit = "<Esc>",
perform_action = "<leader>s",
perform_linewise_action = "<leader>l",
width = "40%",
height = "60%",
border = "rounded",
opacity = 1.0,
edit = nil,
reply = nil,
comment = nil,
note = nil,
help = nil,
pipeline = nil,
squash_message = nil,
backup_register = nil,
},
discussion_tree = {
auto_open = true,
switch_view = "S",
default_view = "discussions",
blacklist = {},
jump_to_file = "o",
jump_to_reviewer = "m",
edit_comment = "e",
delete_comment = "dd",
open_in_browser = "b",
reply = "r",
toggle_node = "t",
add_emoji = "Ea",
delete_emoji = "Ed",
toggle_all_discussions = "T",
toggle_resolved_discussions = "R",
toggle_unresolved_discussions = "U",
keep_current_open = false,
toggle_resolved = "p",
relative = "editor",
position = "left",
size = "20%",
resolved = "",
unresolved = "-",
tree_type = "simple",
toggle_tree_type = "i",
---@param t WinbarTable
winbar = function(t)
local discussions_content = t.resolvable_discussions ~= 0
and string.format("Discussions (%d/%d)", t.resolved_discussions, t.resolvable_discussions)
or "Discussions"
local notes_content = t.resolvable_notes ~= 0
and string.format("Notes (%d/%d)", t.resolved_notes, t.resolvable_notes)
or "Notes"
if t.name == "Discussions" then
notes_content = "%#Comment#" .. notes_content
discussions_content = "%#Text#" .. discussions_content
else
discussions_content = "%#Comment#" .. discussions_content
notes_content = "%#Text#" .. notes_content
end
local help = "%#Comment#%=Help: " .. t.help_keymap:gsub(" ", "<space>") .. " "
return " " .. discussions_content .. " %#Comment#| " .. notes_content .. help
end,
},
merge = {
squash = false,
delete_branch = false,
},
create_mr = {
target = nil,
template_file = nil,
title_input = {
width = 40,
border = "rounded",
},
},
info = {
enabled = true,
horizontal = false,
fields = {
"author",
"created_at",
"updated_at",
"merge_status",
"draft",
"conflicts",
"assignees",
"reviewers",
"pipeline",
"branch",
"target_branch",
"labels",
},
},
discussion_signs = {
enabled = true,
skip_resolved_discussion = false,
severity = vim.diagnostic.severity.INFO,
virtual_text = false,
icons = {
comment = "→|",
range = " |",
},
skip_old_revision_discussion = false,
priority = 100,
},
pipeline = {
created = "",
pending = "",
preparing = "",
scheduled = "",
running = "",
canceled = "",
skipped = "",
success = "",
failed = "",
},
go_server_running = false,
is_gitlab_project = false,
colors = {
discussion_tree = {
username = "Keyword",
date = "Comment",
chevron = "DiffviewNonText",
directory = "Directory",
directory_icon = "DiffviewFolderSign",
file_name = "Normal",
resolved = "DiagnosticSignOk",
unresolved = "DiagnosticSignWarn",
},
},
}
-- These are the initial states of the discussion trees
M.discussion_tree = {
resolved_expanded = false,
unresolved_expanded = false,
}
M.unlinked_discussion_tree = {
resolved_expanded = false,
unresolved_expanded = false,
}
-- Merges user settings into the default settings, overriding them
M.merge_settings = function(args)
M.settings = u.merge(M.settings, args)
-- Check deprecated settings and alert users!
if M.settings.dialogue ~= nil then
u.notify("The dialogue field has been deprecated, please remove it from your setup function", vim.log.levels.WARN)
end
if M.settings.reviewer == "delta" then
u.notify(
"Delta is no longer a supported reviewer, please use diffview and update your setup function",
vim.log.levels.ERROR
)
return false
end
local diffview_ok, _ = pcall(require, "diffview")
if not diffview_ok then
u.notify("Please install diffview, it is required")
return false
end
if M.settings.review_pane ~= nil then
u.notify(
"The review_pane field is only relevant for Delta, which has been deprecated, please remove it from your setup function",
vim.log.levels.WARN
)
end
M.settings.file_separator = (u.is_windows() and "\\" or "/")
return true
end
M.print_settings = function()
vim.print(M.settings)
end
-- First reads environment variables into the settings module,
-- then attemps to read a `.gitlab.nvim` configuration file.
-- If after doing this, any variables are missing, alerts the user.
-- The `.gitlab.nvim` configuration file takes precedence.
M.setPluginConfiguration = function()
if M.initialized then
return true
end
local base_path
if M.settings.config_path ~= nil then
base_path = M.settings.config_path
else
base_path = vim.fn.trim(vim.fn.system({ "git", "rev-parse", "--show-toplevel" }))
if vim.v.shell_error ~= 0 then
u.notify(string.format("Could not get base directory: %s", base_path), vim.log.levels.ERROR)
return false
end
end
local config_file_path = base_path .. M.settings.file_separator .. ".gitlab.nvim"
local config_file_content = u.read_file(config_file_path, { remove_newlines = true })
local file_properties = {}
if config_file_content ~= nil then
local file = assert(io.open(config_file_path, "r"))
for line in file:lines() do
for key, value in string.gmatch(line, "(.-)=(.-)$") do
file_properties[key] = value
end
end
end
M.settings.auth_token = file_properties.auth_token or os.getenv("GITLAB_TOKEN")
M.settings.gitlab_url = u.trim_slash(file_properties.gitlab_url or os.getenv("GITLAB_URL") or "https://gitlab.com")
if M.settings.auth_token == nil then
vim.notify(
"Missing authentication token for Gitlab, please provide it as an environment variable or in the .gitlab.nvim file",
vim.log.levels.ERROR
)
return false
end
M.initialized = true
return true
end
local function exit(popup, opts)
if opts.action_before_exit and opts.cb ~= nil then
opts.cb()
popup:unmount()
else
popup:unmount()
if opts.cb ~= nil then
opts.cb()
end
end
end
-- These keymaps are buffer specific and are set dynamically when popups mount
M.set_popup_keymaps = function(popup, action, linewise_action, opts)
if opts == nil then
opts = {}
end
vim.keymap.set("n", M.settings.popup.exit, function()
exit(popup, opts)
end, { buffer = popup.bufnr, desc = "Exit popup" })
if action ~= "Help" then -- Don't show help on the help popup
vim.keymap.set("n", M.settings.help, function()
local help = require("gitlab.actions.help")
help.open()
end, { buffer = popup.bufnr, desc = "Open help" })
end
if action ~= nil then
vim.keymap.set("n", M.settings.popup.perform_action, function()
local text = u.get_buffer_text(popup.bufnr)
if M.settings.popup.backup_register ~= nil then
vim.cmd("0,$yank " .. M.settings.popup.backup_register)
end
if opts.action_before_close then
action(text, popup.bufnr)
exit(popup, opts)
else
exit(popup, opts)
action(text, popup.bufnr)
end
end, { buffer = popup.bufnr, desc = "Perform action" })
end
if linewise_action ~= nil then
vim.keymap.set("n", M.settings.popup.perform_linewise_action, function()
local bufnr = vim.api.nvim_get_current_buf()
local linnr = vim.api.nvim_win_get_cursor(0)[1]
local text = u.get_line_content(bufnr, linnr)
linewise_action(text)
end, { buffer = popup.bufnr, desc = "Perform linewise action" })
end
end
-- Dependencies
-- These tables are passed to the async.sequence function, which calls them in sequence
-- before calling an action. They are used to set global state that's required
-- for each of the actions to occur. This is necessary because some Gitlab behaviors (like
-- adding a reviewer) requires some initial state.
M.dependencies = {
user = { endpoint = "/users/me", key = "user", state = "USER", refresh = false },
info = { endpoint = "/mr/info", key = "info", state = "INFO", refresh = false },
labels = { endpoint = "/mr/label", key = "labels", state = "LABELS", refresh = false },
revisions = { endpoint = "/mr/revisions", key = "Revisions", state = "MR_REVISIONS", refresh = false },
project_members = {
endpoint = "/project/members",
key = "ProjectMembers",
state = "PROJECT_MEMBERS",
refresh = false,
},
}
-- This function clears out all of the previously fetched data. It's used
-- to reset the plugin state when the Go server is restarted
M.clear_data = function()
M.INFO = nil
for _, dep in ipairs(M.dependencies) do
M[dep.state] = nil
end
end
return M