Release (#415)
fix: parse dates without timezone offset (#404) fix: enable replying if tree is in a different tab (#407) fix: wrong get url (#413) fix: Restore cursor when updating from outside of tree (#406) --------- Co-authored-by: Jakub F. Bortlík <jakub.bortlik@proton.me> Co-authored-by: Oscar <oscar.creator13@gmail.com>
This commit is contained in:
committed by
GitHub
parent
341d56a1cb
commit
30daecfb60
@@ -36,7 +36,7 @@ Extracts information about the current repository and returns
|
|||||||
it to the client for initialization. The current directory must be a valid
|
it to the client for initialization. The current directory must be a valid
|
||||||
Gitlab project and the branch must be a feature branch
|
Gitlab project and the branch must be a feature branch
|
||||||
*/
|
*/
|
||||||
func NewGitData(remote string, g GitManager) (GitData, error) {
|
func NewGitData(remote string, gitlabUrl string, g GitManager) (GitData, error) {
|
||||||
err := g.RefreshProjectInfo(remote)
|
err := g.RefreshProjectInfo(remote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GitData{}, fmt.Errorf("could not get latest information from remote: %v", err)
|
return GitData{}, fmt.Errorf("could not get latest information from remote: %v", err)
|
||||||
@@ -65,7 +65,14 @@ func NewGitData(remote string, g GitManager) (GitData, error) {
|
|||||||
return GitData{}, fmt.Errorf("invalid git URL format: %s", url)
|
return GitData{}, fmt.Errorf("invalid git URL format: %s", url)
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace := matches[1]
|
// remove part of the hostname from the parsed namespace
|
||||||
|
url_re := regexp.MustCompile(`[^\/]\/([^\/].*)$`)
|
||||||
|
url_matches := url_re.FindStringSubmatch(gitlabUrl)
|
||||||
|
var namespace string = matches[1]
|
||||||
|
if len(url_matches) == 2 {
|
||||||
|
namespace = strings.TrimLeft(strings.TrimPrefix(namespace, url_matches[1]), "/")
|
||||||
|
}
|
||||||
|
|
||||||
projectName := matches[2]
|
projectName := matches[2]
|
||||||
|
|
||||||
branchName, err := g.GetCurrentBranchNameFromNativeGitCmd()
|
branchName, err := g.GetCurrentBranchNameFromNativeGitCmd()
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ func (f FakeGitManager) GetProjectUrlFromNativeGitCmd(string) (url string, err e
|
|||||||
|
|
||||||
type TestCase struct {
|
type TestCase struct {
|
||||||
desc string
|
desc string
|
||||||
|
url string
|
||||||
branch string
|
branch string
|
||||||
projectName string
|
projectName string
|
||||||
namespace string
|
namespace string
|
||||||
@@ -40,6 +41,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
testCases := []TestCase{
|
testCases := []TestCase{
|
||||||
{
|
{
|
||||||
desc: "Project configured in SSH under a single folder",
|
desc: "Project configured in SSH under a single folder",
|
||||||
|
url: "git@custom-gitlab.com",
|
||||||
remote: "git@custom-gitlab.com:namespace-1/project-name.git",
|
remote: "git@custom-gitlab.com:namespace-1/project-name.git",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -47,6 +49,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in SSH under a single folder without .git extension",
|
desc: "Project configured in SSH under a single folder without .git extension",
|
||||||
|
url: "git@custom-gitlab.com",
|
||||||
remote: "git@custom-gitlab.com:namespace-1/project-name",
|
remote: "git@custom-gitlab.com:namespace-1/project-name",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -54,6 +57,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in SSH under one nested folder",
|
desc: "Project configured in SSH under one nested folder",
|
||||||
|
url: "git@custom-gitlab.com",
|
||||||
remote: "git@custom-gitlab.com:namespace-1/namespace-2/project-name.git",
|
remote: "git@custom-gitlab.com:namespace-1/namespace-2/project-name.git",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -61,6 +65,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in SSH under two nested folders",
|
desc: "Project configured in SSH under two nested folders",
|
||||||
|
url: "git@custom-gitlab.com",
|
||||||
remote: "git@custom-gitlab.com:namespace-1/namespace-2/namespace-3/project-name.git",
|
remote: "git@custom-gitlab.com:namespace-1/namespace-2/namespace-3/project-name.git",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -68,6 +73,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in SSH:// under a single folder",
|
desc: "Project configured in SSH:// under a single folder",
|
||||||
|
url: "ssh://custom-gitlab.com",
|
||||||
remote: "ssh://custom-gitlab.com/namespace-1/project-name.git",
|
remote: "ssh://custom-gitlab.com/namespace-1/project-name.git",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -75,6 +81,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in SSH:// under a single folder without .git extension",
|
desc: "Project configured in SSH:// under a single folder without .git extension",
|
||||||
|
url: "ssh://custom-gitlab.com",
|
||||||
remote: "ssh://custom-gitlab.com/namespace-1/project-name",
|
remote: "ssh://custom-gitlab.com/namespace-1/project-name",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -82,6 +89,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in SSH:// under two nested folders",
|
desc: "Project configured in SSH:// under two nested folders",
|
||||||
|
url: "ssh://custom-gitlab.com",
|
||||||
remote: "ssh://custom-gitlab.com/namespace-1/namespace-2/namespace-3/project-name.git",
|
remote: "ssh://custom-gitlab.com/namespace-1/namespace-2/namespace-3/project-name.git",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -89,13 +97,23 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in SSH:// and have a custom port",
|
desc: "Project configured in SSH:// and have a custom port",
|
||||||
|
url: "ssh://custom-gitlab.com",
|
||||||
remote: "ssh://custom-gitlab.com:2222/namespace-1/project-name",
|
remote: "ssh://custom-gitlab.com:2222/namespace-1/project-name",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
namespace: "namespace-1",
|
namespace: "namespace-1",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "Project configured in SSH:// and have a custom port (with gitlab url namespace)",
|
||||||
|
url: "ssh://custom-gitlab.com/a",
|
||||||
|
remote: "ssh://custom-gitlab.com:2222/a/namespace-1/project-name",
|
||||||
|
branch: "feature/abc",
|
||||||
|
projectName: "project-name",
|
||||||
|
namespace: "namespace-1",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in HTTP and under a single folder without .git extension",
|
desc: "Project configured in HTTP and under a single folder without .git extension",
|
||||||
|
url: "http://custom-gitlab.com",
|
||||||
remote: "http://custom-gitlab.com/namespace-1/project-name",
|
remote: "http://custom-gitlab.com/namespace-1/project-name",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -103,6 +121,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in HTTP and under a single folder without .git extension (with embedded credentials)",
|
desc: "Project configured in HTTP and under a single folder without .git extension (with embedded credentials)",
|
||||||
|
url: "http://custom-gitlab.com",
|
||||||
remote: "http://username:password@custom-gitlab.com/namespace-1/project-name",
|
remote: "http://username:password@custom-gitlab.com/namespace-1/project-name",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -110,6 +129,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in HTTPS and under a single folder",
|
desc: "Project configured in HTTPS and under a single folder",
|
||||||
|
url: "https://custom-gitlab.com",
|
||||||
remote: "https://custom-gitlab.com/namespace-1/project-name.git",
|
remote: "https://custom-gitlab.com/namespace-1/project-name.git",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -117,6 +137,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in HTTPS and under a single folder (with embedded credentials)",
|
desc: "Project configured in HTTPS and under a single folder (with embedded credentials)",
|
||||||
|
url: "https://custom-gitlab.com",
|
||||||
remote: "https://username:password@custom-gitlab.com/namespace-1/project-name.git",
|
remote: "https://username:password@custom-gitlab.com/namespace-1/project-name.git",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -124,6 +145,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in HTTPS and under a nested folder",
|
desc: "Project configured in HTTPS and under a nested folder",
|
||||||
|
url: "https://custom-gitlab.com",
|
||||||
remote: "https://custom-gitlab.com/namespace-1/namespace-2/project-name.git",
|
remote: "https://custom-gitlab.com/namespace-1/namespace-2/project-name.git",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -131,6 +153,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in HTTPS and under a nested folder (with embedded credentials)",
|
desc: "Project configured in HTTPS and under a nested folder (with embedded credentials)",
|
||||||
|
url: "https://custom-gitlab.com",
|
||||||
remote: "https://username:password@custom-gitlab.com/namespace-1/namespace-2/project-name.git",
|
remote: "https://username:password@custom-gitlab.com/namespace-1/namespace-2/project-name.git",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -138,6 +161,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in HTTPS and under two nested folders",
|
desc: "Project configured in HTTPS and under two nested folders",
|
||||||
|
url: "https://custom-gitlab.com",
|
||||||
remote: "https://custom-gitlab.com/namespace-1/namespace-2/namespace-3/project-name.git",
|
remote: "https://custom-gitlab.com/namespace-1/namespace-2/namespace-3/project-name.git",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
@@ -145,11 +169,28 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Project configured in HTTPS and under two nested folders (with embedded credentials)",
|
desc: "Project configured in HTTPS and under two nested folders (with embedded credentials)",
|
||||||
|
url: "https://custom-gitlab.com",
|
||||||
remote: "https://username:password@custom-gitlab.com/namespace-1/namespace-2/namespace-3/project-name.git",
|
remote: "https://username:password@custom-gitlab.com/namespace-1/namespace-2/namespace-3/project-name.git",
|
||||||
branch: "feature/abc",
|
branch: "feature/abc",
|
||||||
projectName: "project-name",
|
projectName: "project-name",
|
||||||
namespace: "namespace-1/namespace-2/namespace-3",
|
namespace: "namespace-1/namespace-2/namespace-3",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "Project configured in HTTPS and under one nested folders (with gitlab url namespace)",
|
||||||
|
url: "https://custom-gitlab.com/gitlab",
|
||||||
|
remote: "https://username:password@custom-gitlab.com/gitlab/namespace-2/namespace-3/project-name.git",
|
||||||
|
branch: "feature/abc",
|
||||||
|
projectName: "project-name",
|
||||||
|
namespace: "namespace-2/namespace-3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Project configured in HTTPS and under one nested folders (with gitlab url namespace + extra slash)",
|
||||||
|
url: "https://custom-gitlab.com/gitlab/",
|
||||||
|
remote: "https://username:password@custom-gitlab.com/gitlab/namespace-2/namespace-3/project-name.git",
|
||||||
|
branch: "feature/abc",
|
||||||
|
projectName: "project-name",
|
||||||
|
namespace: "namespace-2/namespace-3",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tC := range testCases {
|
for _, tC := range testCases {
|
||||||
t.Run(tC.desc, func(t *testing.T) {
|
t.Run(tC.desc, func(t *testing.T) {
|
||||||
@@ -159,7 +200,7 @@ func TestExtractGitInfo_Success(t *testing.T) {
|
|||||||
BranchName: tC.branch,
|
BranchName: tC.branch,
|
||||||
RemoteUrl: tC.remote,
|
RemoteUrl: tC.remote,
|
||||||
}
|
}
|
||||||
data, err := NewGitData(tC.remote, g)
|
data, err := NewGitData(tC.remote, tC.url, g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("No error was expected, got %s", err)
|
t.Errorf("No error was expected, got %s", err)
|
||||||
}
|
}
|
||||||
@@ -204,7 +245,7 @@ func TestExtractGitInfo_FailToGetProjectRemoteUrl(t *testing.T) {
|
|||||||
g := failingUrlManager{
|
g := failingUrlManager{
|
||||||
errMsg: tC.errMsg,
|
errMsg: tC.errMsg,
|
||||||
}
|
}
|
||||||
_, err := NewGitData("", g)
|
_, err := NewGitData("", "", g)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Expected an error, got none")
|
t.Errorf("Expected an error, got none")
|
||||||
}
|
}
|
||||||
@@ -236,7 +277,7 @@ func TestExtractGitInfo_FailToGetCurrentBranchName(t *testing.T) {
|
|||||||
},
|
},
|
||||||
errMsg: tC.errMsg,
|
errMsg: tC.errMsg,
|
||||||
}
|
}
|
||||||
_, err := NewGitData("", g)
|
_, err := NewGitData("", "", g)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Expected an error, got none")
|
t.Errorf("Expected an error, got none")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gitManager := git.Git{}
|
gitManager := git.Git{}
|
||||||
gitData, err := git.NewGitData(pluginOptions.ConnectionSettings.Remote, gitManager)
|
gitData, err := git.NewGitData(pluginOptions.ConnectionSettings.Remote, pluginOptions.GitlabUrl, gitManager)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failure initializing plugin: %v", err)
|
log.Fatalf("Failure initializing plugin: %v", err)
|
||||||
|
|||||||
@@ -9661,6 +9661,25 @@
|
|||||||
],
|
],
|
||||||
"moji": "😅"
|
"moji": "😅"
|
||||||
},
|
},
|
||||||
|
"tada": {
|
||||||
|
"unicode": "1F389",
|
||||||
|
"unicode_alternates": [],
|
||||||
|
"name": "party popper as a 'tada' celebration",
|
||||||
|
"shortname": ":tada:",
|
||||||
|
"category": "people",
|
||||||
|
"aliases": [
|
||||||
|
":party_popper:"
|
||||||
|
],
|
||||||
|
"aliases_ascii": [],
|
||||||
|
"keywords": [
|
||||||
|
"celebrate",
|
||||||
|
"celebration",
|
||||||
|
"hooray",
|
||||||
|
"hurrah",
|
||||||
|
"hurray"
|
||||||
|
],
|
||||||
|
"moji": "🎉"
|
||||||
|
},
|
||||||
"thermometer_face": {
|
"thermometer_face": {
|
||||||
"unicode": "1F912",
|
"unicode": "1F912",
|
||||||
"unicode_alternates": [],
|
"unicode_alternates": [],
|
||||||
|
|||||||
@@ -41,14 +41,8 @@ local confirm_create_comment = function(text, visual_range, unlinked, discussion
|
|||||||
local body = { discussion_id = discussion_id, reply = text, draft = is_draft }
|
local body = { discussion_id = discussion_id, reply = text, draft = is_draft }
|
||||||
job.run_job("/mr/reply", "POST", body, function()
|
job.run_job("/mr/reply", "POST", body, function()
|
||||||
u.notify("Sent reply!", vim.log.levels.INFO)
|
u.notify("Sent reply!", vim.log.levels.INFO)
|
||||||
if is_draft then
|
|
||||||
draft_notes.load_draft_notes(function()
|
|
||||||
discussions.rebuild_view(unlinked)
|
discussions.rebuild_view(unlinked)
|
||||||
end)
|
end)
|
||||||
else
|
|
||||||
discussions.rebuild_view(unlinked)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -69,8 +63,6 @@ local confirm_create_comment = function(text, visual_range, unlinked, discussion
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.print("Here: ", unlinked, discussion_id)
|
|
||||||
|
|
||||||
local reviewer_data = reviewer.get_reviewer_data()
|
local reviewer_data = reviewer.get_reviewer_data()
|
||||||
if reviewer_data == nil then
|
if reviewer_data == nil then
|
||||||
u.notify("Error getting reviewer data", vim.log.levels.ERROR)
|
u.notify("Error getting reviewer data", vim.log.levels.ERROR)
|
||||||
@@ -102,7 +94,7 @@ local confirm_create_comment = function(text, visual_range, unlinked, discussion
|
|||||||
job.run_job("/mr/draft_notes/", "POST", body, function()
|
job.run_job("/mr/draft_notes/", "POST", body, function()
|
||||||
u.notify("Draft reply created!", vim.log.levels.INFO)
|
u.notify("Draft reply created!", vim.log.levels.INFO)
|
||||||
draft_notes.load_draft_notes(function()
|
draft_notes.load_draft_notes(function()
|
||||||
discussions.rebuild_view(false, true)
|
discussions.rebuild_view(unlinked)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
return
|
return
|
||||||
@@ -166,7 +158,7 @@ end
|
|||||||
---@param opts LayoutOpts
|
---@param opts LayoutOpts
|
||||||
---@return NuiLayout|nil
|
---@return NuiLayout|nil
|
||||||
M.create_comment_layout = function(opts)
|
M.create_comment_layout = function(opts)
|
||||||
if opts.unlinked ~= true then
|
if opts.unlinked ~= true and opts.discussion_id == nil then
|
||||||
-- Check that diffview is initialized
|
-- Check that diffview is initialized
|
||||||
if reviewer.tabnr == nil then
|
if reviewer.tabnr == nil then
|
||||||
u.notify("Reviewer must be initialized first", vim.log.levels.ERROR)
|
u.notify("Reviewer must be initialized first", vim.log.levels.ERROR)
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ M.open = function(callback)
|
|||||||
local current_window = vim.api.nvim_get_current_win() -- Save user's current window in case they switched while content was loading
|
local current_window = vim.api.nvim_get_current_win() -- Save user's current window in case they switched while content was loading
|
||||||
vim.api.nvim_set_current_win(M.split.winid)
|
vim.api.nvim_set_current_win(M.split.winid)
|
||||||
|
|
||||||
common.switch_can_edit_bufs(true, M.linked_bufnr, M.unliked_bufnr)
|
common.switch_can_edit_bufs(true, M.linked_bufnr, M.unlinked_bufnr)
|
||||||
M.rebuild_discussion_tree()
|
M.rebuild_discussion_tree()
|
||||||
M.rebuild_unlinked_discussion_tree()
|
M.rebuild_unlinked_discussion_tree()
|
||||||
|
|
||||||
@@ -432,6 +432,9 @@ M.rebuild_discussion_tree = function()
|
|||||||
if M.linked_bufnr == nil then
|
if M.linked_bufnr == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local current_node = discussions_tree.get_node_at_cursor(M.discussion_tree, M.last_node_at_cursor)
|
||||||
|
|
||||||
local expanded_node_ids = M.gather_expanded_node_ids(M.discussion_tree)
|
local expanded_node_ids = M.gather_expanded_node_ids(M.discussion_tree)
|
||||||
common.switch_can_edit_bufs(true, M.linked_bufnr, M.unlinked_bufnr)
|
common.switch_can_edit_bufs(true, M.linked_bufnr, M.unlinked_bufnr)
|
||||||
|
|
||||||
@@ -447,12 +450,14 @@ M.rebuild_discussion_tree = function()
|
|||||||
bufnr = M.linked_bufnr,
|
bufnr = M.linked_bufnr,
|
||||||
prepare_node = tree_utils.nui_tree_prepare_node,
|
prepare_node = tree_utils.nui_tree_prepare_node,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Re-expand already expanded nodes
|
-- Re-expand already expanded nodes
|
||||||
for _, id in ipairs(expanded_node_ids) do
|
for _, id in ipairs(expanded_node_ids) do
|
||||||
tree_utils.open_node_by_id(discussion_tree, id)
|
tree_utils.open_node_by_id(discussion_tree, id)
|
||||||
end
|
end
|
||||||
|
|
||||||
discussion_tree:render()
|
discussion_tree:render()
|
||||||
|
discussions_tree.restore_cursor_position(M.split.winid, discussion_tree, current_node)
|
||||||
|
|
||||||
M.set_tree_keymaps(discussion_tree, M.linked_bufnr, false)
|
M.set_tree_keymaps(discussion_tree, M.linked_bufnr, false)
|
||||||
M.discussion_tree = discussion_tree
|
M.discussion_tree = discussion_tree
|
||||||
common.switch_can_edit_bufs(false, M.linked_bufnr, M.unlinked_bufnr)
|
common.switch_can_edit_bufs(false, M.linked_bufnr, M.unlinked_bufnr)
|
||||||
@@ -466,6 +471,9 @@ M.rebuild_unlinked_discussion_tree = function()
|
|||||||
if M.unlinked_bufnr == nil then
|
if M.unlinked_bufnr == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local current_node = discussions_tree.get_node_at_cursor(M.unlinked_discussion_tree, M.last_node_at_cursor)
|
||||||
|
|
||||||
local expanded_node_ids = M.gather_expanded_node_ids(M.unlinked_discussion_tree)
|
local expanded_node_ids = M.gather_expanded_node_ids(M.unlinked_discussion_tree)
|
||||||
common.switch_can_edit_bufs(true, M.linked_bufnr, M.unlinked_bufnr)
|
common.switch_can_edit_bufs(true, M.linked_bufnr, M.unlinked_bufnr)
|
||||||
vim.api.nvim_buf_set_lines(M.unlinked_bufnr, 0, -1, false, {})
|
vim.api.nvim_buf_set_lines(M.unlinked_bufnr, 0, -1, false, {})
|
||||||
@@ -487,6 +495,7 @@ M.rebuild_unlinked_discussion_tree = function()
|
|||||||
tree_utils.open_node_by_id(unlinked_discussion_tree, id)
|
tree_utils.open_node_by_id(unlinked_discussion_tree, id)
|
||||||
end
|
end
|
||||||
unlinked_discussion_tree:render()
|
unlinked_discussion_tree:render()
|
||||||
|
discussions_tree.restore_cursor_position(M.split.winid, unlinked_discussion_tree, current_node)
|
||||||
|
|
||||||
M.set_tree_keymaps(unlinked_discussion_tree, M.unlinked_bufnr, true)
|
M.set_tree_keymaps(unlinked_discussion_tree, M.unlinked_bufnr, true)
|
||||||
M.unlinked_discussion_tree = unlinked_discussion_tree
|
M.unlinked_discussion_tree = unlinked_discussion_tree
|
||||||
@@ -535,6 +544,14 @@ M.create_split_and_bufs = function()
|
|||||||
buffer = linked_bufnr,
|
buffer = linked_bufnr,
|
||||||
callback = function()
|
callback = function()
|
||||||
M.last_row, M.last_column = unpack(vim.api.nvim_win_get_cursor(0))
|
M.last_row, M.last_column = unpack(vim.api.nvim_win_get_cursor(0))
|
||||||
|
M.last_node_at_cursor = M.discussion_tree and M.discussion_tree:get_node() or nil
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd("WinLeave", {
|
||||||
|
buffer = unlinked_bufnr,
|
||||||
|
callback = function()
|
||||||
|
M.last_node_at_cursor = M.unlinked_discussion_tree and M.unlinked_discussion_tree:get_node() or nil
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -422,13 +422,37 @@ M.toggle_nodes = function(winid, tree, unlinked, opts)
|
|||||||
M.restore_cursor_position(winid, tree, current_node, root_node)
|
M.restore_cursor_position(winid, tree, current_node, root_node)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Get current node for restoring cursor position
|
||||||
|
---@param tree NuiTree The inline discussion tree or the unlinked discussion tree
|
||||||
|
---@param last_node NuiTree.Node|nil The last active discussion tree node in case we are not in any of the discussion trees
|
||||||
|
M.get_node_at_cursor = function(tree, last_node)
|
||||||
|
if tree == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if vim.api.nvim_get_current_win() == vim.fn.win_findbuf(tree.bufnr)[1] then
|
||||||
|
return tree:get_node()
|
||||||
|
else
|
||||||
|
return last_node
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
---Restore cursor position to the original node if possible
|
---Restore cursor position to the original node if possible
|
||||||
|
---@param winid integer Window number of the discussions split
|
||||||
|
---@param tree NuiTree The inline discussion tree or the unlinked discussion tree
|
||||||
|
---@param original_node NuiTree.Node|nil The last node with the cursor
|
||||||
|
---@param root_node NuiTree.Node|nil The root node of the last node with the cursor
|
||||||
M.restore_cursor_position = function(winid, tree, original_node, root_node)
|
M.restore_cursor_position = function(winid, tree, original_node, root_node)
|
||||||
|
if original_node == nil or tree == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
local _, line_number = tree:get_node("-" .. tostring(original_node.id))
|
local _, line_number = tree:get_node("-" .. tostring(original_node.id))
|
||||||
-- If current_node is has been collapsed, get line number of root node instead
|
-- If current_node has been collapsed, try to get line number of root node instead
|
||||||
if line_number == nil and root_node then
|
if line_number == nil then
|
||||||
|
root_node = root_node and root_node or common.get_root_node(tree, original_node)
|
||||||
|
if root_node ~= nil then
|
||||||
_, line_number = tree:get_node("-" .. tostring(root_node.id))
|
_, line_number = tree:get_node("-" .. tostring(root_node.id))
|
||||||
end
|
end
|
||||||
|
end
|
||||||
if line_number ~= nil then
|
if line_number ~= nil then
|
||||||
vim.api.nvim_win_set_cursor(winid, { line_number, 0 })
|
vim.api.nvim_win_set_cursor(winid, { line_number, 0 })
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ M.build_info_lines = function()
|
|||||||
local options = {
|
local options = {
|
||||||
author = { title = "Author", content = "@" .. info.author.username .. " (" .. info.author.name .. ")" },
|
author = { title = "Author", content = "@" .. info.author.username .. " (" .. info.author.name .. ")" },
|
||||||
created_at = { title = "Created", content = u.format_to_local(info.created_at, vim.fn.strftime("%z")) },
|
created_at = { title = "Created", content = u.format_to_local(info.created_at, vim.fn.strftime("%z")) },
|
||||||
updated_at = { title = "Updated", content = u.format_to_local(info.updated_at, vim.fn.strftime("%z")) },
|
updated_at = { title = "Updated", content = u.time_since(info.updated_at) },
|
||||||
detailed_merge_status = { title = "Status", content = info.detailed_merge_status },
|
detailed_merge_status = { title = "Status", content = info.detailed_merge_status },
|
||||||
draft = { title = "Draft", content = (info.draft and "Yes" or "No") },
|
draft = { title = "Draft", content = (info.draft and "Yes" or "No") },
|
||||||
conflicts = { title = "Merge Conflicts", content = (info.has_conflicts and "Yes" or "No") },
|
conflicts = { title = "Merge Conflicts", content = (info.has_conflicts and "Yes" or "No") },
|
||||||
|
|||||||
@@ -254,6 +254,20 @@ M.format_to_local = function(date_string, offset)
|
|||||||
local tzOffsetSign, tzOffsetHour, tzOffsetMin
|
local tzOffsetSign, tzOffsetHour, tzOffsetMin
|
||||||
year, month, day, hour, min, sec, _, tzOffsetSign, tzOffsetHour, tzOffsetMin =
|
year, month, day, hour, min, sec, _, tzOffsetSign, tzOffsetHour, tzOffsetMin =
|
||||||
date_string:match("(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+).(%d+)([%+%-])(%d%d):(%d%d)")
|
date_string:match("(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+).(%d+)([%+%-])(%d%d):(%d%d)")
|
||||||
|
|
||||||
|
-- ISO 8601 format with just "Z" (aka no time offset)
|
||||||
|
-- 2021-01-01T00:00:00Z
|
||||||
|
if year == nil then
|
||||||
|
year, month, day, hour, min, sec = date_string:match("(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z")
|
||||||
|
tzOffsetSign = "-"
|
||||||
|
tzOffsetHour = "00"
|
||||||
|
tzOffsetMin = "00"
|
||||||
|
end
|
||||||
|
|
||||||
|
if year == nil then
|
||||||
|
return "Date Unparseable"
|
||||||
|
end
|
||||||
|
|
||||||
tzOffset = tzOffsetSign .. tzOffsetHour .. tzOffsetMin
|
tzOffset = tzOffsetSign .. tzOffsetHour .. tzOffsetMin
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,13 @@ describe("utils/init.lua", function()
|
|||||||
local want = "November 19, 2011"
|
local want = "November 19, 2011"
|
||||||
assert.are.same(want, got)
|
assert.are.same(want, got)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it("Parses TZ w/out offset (relative)", function()
|
||||||
|
local stamp = "2023-11-14T18:44:02Z"
|
||||||
|
local got = u.time_since(stamp, current_date)
|
||||||
|
local want = "5 days ago"
|
||||||
|
assert.are.same(want, got)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe("remove_first_value", function()
|
describe("remove_first_value", function()
|
||||||
@@ -209,6 +216,7 @@ describe("utils/init.lua", function()
|
|||||||
{ "2016-11-21T20:25:09.482-05:00", "-0500", "11/21/2016 at 20:25" },
|
{ "2016-11-21T20:25:09.482-05:00", "-0500", "11/21/2016 at 20:25" },
|
||||||
{ "2016-11-22T1:25:09.482-00:00", "-0000", "11/22/2016 at 01:25" },
|
{ "2016-11-22T1:25:09.482-00:00", "-0000", "11/22/2016 at 01:25" },
|
||||||
{ "2017-3-22T20:25:09.482+07:00", "+0700", "03/22/2017 at 20:25" },
|
{ "2017-3-22T20:25:09.482+07:00", "+0700", "03/22/2017 at 20:25" },
|
||||||
|
{ "2016-11-22T1:25:09Z", "-0000", "11/22/2016 at 01:25" },
|
||||||
}
|
}
|
||||||
for _, val in ipairs(tests) do
|
for _, val in ipairs(tests) do
|
||||||
local got = u.format_to_local(val[1], val[2])
|
local got = u.format_to_local(val[1], val[2])
|
||||||
|
|||||||
Reference in New Issue
Block a user