Skip to content

Commit cf6ccdd

Browse files
harrisoncramerjakubbortliksunfuzepatrickpichler
authored
Release 2.5.1 (#271)
* feat: Support for custom authentication provider functions (#270) * feat: Support for adding "draft" notes to the review, and publishing them, either individually or all at once. Addresses feature request #223. * feat: Lets users select + checkout a merge request directly within Neovim, without exiting to the terminal * fix: Checks that the remote feature branch exists and is up-to-date before creating a MR, starting a review, or opening the MR summary (#278) * docs: We require some state from Diffview, this shows how to load that state prior to installing w/ Packer. Fixes #94. This is a #MINOR release. --------- Co-authored-by: Jakub F. Bortlík <[email protected]> Co-authored-by: sunfuze <[email protected]> Co-authored-by: Patrick Pichler <[email protected]>
1 parent f10c4eb commit cf6ccdd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2823
-1142
lines changed

README.md

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ To view these help docs and to get more detailed help information, please run `:
2525

2626
1. Install Go
2727
2. Add configuration (see Installation section)
28-
3. Checkout your feature branch: `git checkout feature-branch`
29-
4. Open Neovim
30-
5. Run `:lua require("gitlab").review()` to open the reviewer pane
28+
5. Run `:lua require("gitlab").choose_merge_request()`
29+
30+
This will checkout the branch locally, and open the plugin's reviewer pane.
3131

3232
For more detailed information about the Lua APIs please run `:h gitlab.nvim.api`
3333

@@ -56,20 +56,25 @@ return {
5656
And with Packer:
5757

5858
```lua
59-
use {
60-
'harrisoncramer/gitlab.nvim',
61-
requires = {
62-
"MunifTanjim/nui.nvim",
63-
"nvim-lua/plenary.nvim",
64-
"sindrets/diffview.nvim",
65-
"stevearc/dressing.nvim", -- Recommended but not required. Better UI for pickers.
66-
"nvim-tree/nvim-web-devicons", -- Recommended but not required. Icons in discussion tree.
67-
},
68-
run = function() require("gitlab.server").build(true) end,
69-
config = function()
70-
require("gitlab").setup()
71-
end,
72-
}
59+
use {
60+
"harrisoncramer/gitlab.nvim",
61+
requires = {
62+
"MunifTanjim/nui.nvim",
63+
"nvim-lua/plenary.nvim",
64+
"sindrets/diffview.nvim"
65+
"stevearc/dressing.nvim", -- Recommended but not required. Better UI for pickers.
66+
"nvim-tree/nvim-web-devicons", -- Recommended but not required. Icons in discussion tree.
67+
},
68+
build = function()
69+
require("gitlab.server").build()
70+
end,
71+
branch = "develop",
72+
config = function()
73+
require("diffview") -- We require some global state from diffview
74+
local gitlab = require("gitlab")
75+
gitlab.setup()
76+
end,
77+
}
7378
```
7479

7580
## Connecting to Gitlab
@@ -92,6 +97,18 @@ gitlab_url=https://my-personal-gitlab-instance.com/
9297

9398
The plugin will look for the `.gitlab.nvim` file in the root of the current project by default. However, you may provide a custom path to the configuration file via the `config_path` option. This must be an absolute path to the directory that holds your `.gitlab.nvim` file.
9499

100+
In case even more control over the auth config is needed, there is the possibility to override the `auth_provider` settings field. It should be
101+
a function that returns the `token` as well as the `gitlab_url` value, and a nilable error. If the `gitlab_url` is `nil`, `https://gitlab.com` is used as default.
102+
103+
Here an example how to use a custom `auth_provider`:
104+
```lua
105+
require("gitlab").setup({
106+
auth_provider = function()
107+
return "my_token", "https://custom.gitlab.instance.url", nil
108+
end,
109+
}
110+
```
111+
95112
For more settings, please see `:h gitlab.nvim.connecting-to-gitlab`
96113

97114
## Configuring the Plugin
@@ -103,7 +120,10 @@ require("gitlab").setup({
103120
port = nil, -- The port of the Go server, which runs in the background, if omitted or `nil` the port will be chosen automatically
104121
log_path = vim.fn.stdpath("cache") .. "/gitlab.nvim.log", -- Log path for the Go server
105122
config_path = nil, -- Custom path for `.gitlab.nvim` file, please read the "Connecting to Gitlab" section
106-
debug = { go_request = false, go_response = false }, -- Which values to log
123+
debug = {
124+
go_request = false,
125+
go_response = false,
126+
},
107127
attachment_dir = nil, -- The local directory for files (see the "summary" section)
108128
reviewer_settings = {
109129
diffview = {
@@ -150,6 +170,7 @@ require("gitlab").setup({
150170
toggle_resolved_discussions = "R", -- Open or close all resolved discussions
151171
toggle_unresolved_discussions = "U", -- Open or close all unresolved discussions
152172
keep_current_open = false, -- If true, current discussion stays open even if it should otherwise be closed when toggling
173+
publish_draft = "P", -- Publishes the currently focused note/comment
153174
toggle_resolved = "p" -- Toggles the resolved status of the whole discussion
154175
position = "left", -- "top", "right", "bottom" or "left"
155176
open_in_browser = "b" -- Jump to the URL of the current note/discussion
@@ -160,9 +181,14 @@ require("gitlab").setup({
160181
unresolved = '-', -- Symbol to show next to unresolved discussions
161182
tree_type = "simple", -- Type of discussion tree - "simple" means just list of discussions, "by_file_name" means file tree with discussions under file
162183
toggle_tree_type = "i", -- Toggle type of discussion tree - "simple", or "by_file_name"
184+
draft_mode = false, -- Whether comments are posted as drafts as part of a review
185+
toggle_draft_mode = "D" -- Toggle between draft mode (comments posted as drafts) and live mode (comments are posted immediately)
163186
winbar = nil -- Custom function to return winbar title, should return a string. Provided with WinbarTable (defined in annotations.lua)
164187
-- If using lualine, please add "gitlab" to disabled file types, otherwise you will not see the winbar.
165188
},
189+
choose_merge_request = {
190+
open_reviewer = true, -- Open the reviewer window automatically after switching merge requests
191+
},
166192
info = { -- Show additional fields in the summary view
167193
enabled = true,
168194
horizontal = false, -- Display metadata to the left of the summary rather than underneath
@@ -246,6 +272,7 @@ you need to set them up yourself. Here's what I'm using:
246272
```lua
247273
local gitlab = require("gitlab")
248274
local gitlab_server = require("gitlab.server")
275+
vim.keymap.set("n", "glb", gitlab.choose_merge_request)
249276
vim.keymap.set("n", "glr", gitlab.review)
250277
vim.keymap.set("n", "gls", gitlab.summary)
251278
vim.keymap.set("n", "glA", gitlab.approve)
@@ -266,6 +293,8 @@ vim.keymap.set("n", "glrd", gitlab.delete_reviewer)
266293
vim.keymap.set("n", "glp", gitlab.pipeline)
267294
vim.keymap.set("n", "glo", gitlab.open_in_browser)
268295
vim.keymap.set("n", "glM", gitlab.merge)
296+
vim.keymap.set("n", "glu", gitlab.copy_mr_url)
297+
vim.keymap.set("n", "glP", gitlab.publish_all_drafts)
269298
```
270299

271300
For more information about each of these commands, and about the APIs in general, run `:h gitlab.nvim.api`

after/syntax/gitlab.vim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ syntax match ChevronDown ""
88
syntax match ChevronRight ""
99
syntax match Resolved /\s\s\?/
1010
syntax match Unresolved /\s-\s\?/
11+
syntax match Pencil //
1112

1213
highlight link Username GitlabUsername
1314
highlight link Date GitlabDate
1415
highlight link ChevronDown GitlabChevron
1516
highlight link ChevronRight GitlabChevron
1617
highlight link Resolved GitlabResolved
1718
highlight link Unresolved GitlabUnresolved
19+
highlight link Pencil GitlabDraft
1820

1921
let b:current_syntax = "gitlab"

cmd/assignee_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ func updateAssigneesErr(pid interface{}, mergeRequest int, opt *gitlab.UpdateMer
2323
func TestAssigneeHandler(t *testing.T) {
2424
t.Run("Updates assignees", func(t *testing.T) {
2525
request := makeRequest(t, http.MethodPut, "/mr/assignee", AssigneeUpdateRequest{Ids: []int{1, 2}})
26-
server, _ := createRouterAndApi(fakeClient{updateMergeRequestFn: updateAssignees})
26+
server, _ := createRouterAndApi(fakeClient{updateMergeRequest: updateAssignees})
2727
data := serveRequest(t, server, request, AssigneeUpdateResponse{})
2828
assert(t, data.SuccessResponse.Message, "Assignees updated")
2929
assert(t, data.SuccessResponse.Status, http.StatusOK)
3030
})
3131

3232
t.Run("Disallows non-PUT method", func(t *testing.T) {
3333
request := makeRequest(t, http.MethodPost, "/mr/assignee", nil)
34-
server, _ := createRouterAndApi(fakeClient{updateMergeRequestFn: updateAssignees})
34+
server, _ := createRouterAndApi(fakeClient{updateMergeRequest: updateAssignees})
3535
data := serveRequest(t, server, request, ErrorResponse{})
3636
assert(t, data.Status, http.StatusMethodNotAllowed)
3737
assert(t, data.Details, "Invalid request type")
@@ -40,7 +40,7 @@ func TestAssigneeHandler(t *testing.T) {
4040

4141
t.Run("Handles errors from Gitlab client", func(t *testing.T) {
4242
request := makeRequest(t, http.MethodPut, "/mr/assignee", AssigneeUpdateRequest{Ids: []int{1, 2}})
43-
server, _ := createRouterAndApi(fakeClient{updateMergeRequestFn: updateAssigneesErr})
43+
server, _ := createRouterAndApi(fakeClient{updateMergeRequest: updateAssigneesErr})
4444
data := serveRequest(t, server, request, ErrorResponse{})
4545
assert(t, data.Status, http.StatusInternalServerError)
4646
assert(t, data.Message, "Could not modify merge request assignees")
@@ -49,7 +49,7 @@ func TestAssigneeHandler(t *testing.T) {
4949

5050
t.Run("Handles non-200s from Gitlab client", func(t *testing.T) {
5151
request := makeRequest(t, http.MethodPut, "/mr/assignee", AssigneeUpdateRequest{Ids: []int{1, 2}})
52-
server, _ := createRouterAndApi(fakeClient{updateMergeRequestFn: updateAssigneesNon200})
52+
server, _ := createRouterAndApi(fakeClient{updateMergeRequest: updateAssigneesNon200})
5353
data := serveRequest(t, server, request, ErrorResponse{})
5454
assert(t, data.Status, http.StatusSeeOther)
5555
assert(t, data.Message, "Could not modify merge request assignees")

cmd/client.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type Client struct {
4040
*gitlab.LabelsService
4141
*gitlab.AwardEmojiService
4242
*gitlab.UsersService
43+
*gitlab.DraftNotesService
4344
}
4445

4546
/* initGitlabClient parses and validates the project settings and initializes the Gitlab client. */
@@ -116,6 +117,7 @@ func initGitlabClient() (error, *Client) {
116117
LabelsService: client.Labels,
117118
AwardEmojiService: client.AwardEmoji,
118119
UsersService: client.Users,
120+
DraftNotesService: client.DraftNotes,
119121
}
120122
}
121123

cmd/comment.go

Lines changed: 15 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package main
22

33
import (
4-
"crypto/sha1"
54
"encoding/json"
65
"fmt"
76
"io"
@@ -11,28 +10,8 @@ import (
1110
)
1211

1312
type PostCommentRequest struct {
14-
Comment string `json:"comment"`
15-
FileName string `json:"file_name"`
16-
NewLine *int `json:"new_line,omitempty"`
17-
OldLine *int `json:"old_line,omitempty"`
18-
HeadCommitSHA string `json:"head_commit_sha"`
19-
BaseCommitSHA string `json:"base_commit_sha"`
20-
StartCommitSHA string `json:"start_commit_sha"`
21-
Type string `json:"type"`
22-
LineRange *LineRange `json:"line_range,omitempty"`
23-
}
24-
25-
/* LineRange represents the range of a note. */
26-
type LineRange struct {
27-
StartRange *LinePosition `json:"start"`
28-
EndRange *LinePosition `json:"end"`
29-
}
30-
31-
/* LinePosition represents a position in a line range. Unlike the Gitlab struct, this does not contain LineCode with a sha1 of the filename */
32-
type LinePosition struct {
33-
Type string `json:"type"`
34-
OldLine int `json:"old_line"`
35-
NewLine int `json:"new_line"`
13+
Comment string `json:"comment"`
14+
PositionData
3615
}
3716

3817
type DeleteCommentRequest struct {
@@ -53,6 +32,15 @@ type CommentResponse struct {
5332
Discussion *gitlab.Discussion `json:"discussion"`
5433
}
5534

35+
/* CommentWithPosition is a comment with an (optional) position data value embedded in it. The position data will be non-nil for range-based comments. */
36+
type CommentWithPosition struct {
37+
PositionData PositionData
38+
}
39+
40+
func (comment CommentWithPosition) GetPositionData() PositionData {
41+
return comment.PositionData
42+
}
43+
5644
/* commentHandler creates, edits, and deletes discussions (comments, multi-line comments) */
5745
func (a *api) commentHandler(w http.ResponseWriter, r *http.Request) {
5846
w.Header().Set("Content-Type", "application/json")
@@ -133,46 +121,10 @@ func (a *api) postComment(w http.ResponseWriter, r *http.Request) {
133121

134122
/* If we are leaving a comment on a line, leave position. Otherwise,
135123
we are leaving a note (unlinked comment) */
136-
var friendlyName = "Note"
124+
137125
if postCommentRequest.FileName != "" {
138-
friendlyName = "Comment"
139-
opt.Position = &gitlab.PositionOptions{
140-
PositionType: &postCommentRequest.Type,
141-
StartSHA: &postCommentRequest.StartCommitSHA,
142-
HeadSHA: &postCommentRequest.HeadCommitSHA,
143-
BaseSHA: &postCommentRequest.BaseCommitSHA,
144-
NewPath: &postCommentRequest.FileName,
145-
OldPath: &postCommentRequest.FileName,
146-
NewLine: postCommentRequest.NewLine,
147-
OldLine: postCommentRequest.OldLine,
148-
}
149-
150-
if postCommentRequest.LineRange != nil {
151-
friendlyName = "Multiline Comment"
152-
shaFormat := "%x_%d_%d"
153-
startFilenameSha := fmt.Sprintf(
154-
shaFormat,
155-
sha1.Sum([]byte(postCommentRequest.FileName)),
156-
postCommentRequest.LineRange.StartRange.OldLine,
157-
postCommentRequest.LineRange.StartRange.NewLine,
158-
)
159-
endFilenameSha := fmt.Sprintf(
160-
shaFormat,
161-
sha1.Sum([]byte(postCommentRequest.FileName)),
162-
postCommentRequest.LineRange.EndRange.OldLine,
163-
postCommentRequest.LineRange.EndRange.NewLine,
164-
)
165-
opt.Position.LineRange = &gitlab.LineRangeOptions{
166-
Start: &gitlab.LinePositionOptions{
167-
Type: &postCommentRequest.LineRange.StartRange.Type,
168-
LineCode: &startFilenameSha,
169-
},
170-
End: &gitlab.LinePositionOptions{
171-
Type: &postCommentRequest.LineRange.EndRange.Type,
172-
LineCode: &endFilenameSha,
173-
},
174-
}
175-
}
126+
commentWithPositionData := CommentWithPosition{postCommentRequest.PositionData}
127+
opt.Position = buildCommentPosition(commentWithPositionData)
176128
}
177129

178130
discussion, res, err := a.client.CreateMergeRequestDiscussion(a.projectInfo.ProjectId, a.projectInfo.MergeId, &opt)
@@ -190,7 +142,7 @@ func (a *api) postComment(w http.ResponseWriter, r *http.Request) {
190142
w.WriteHeader(http.StatusOK)
191143
response := CommentResponse{
192144
SuccessResponse: SuccessResponse{
193-
Message: fmt.Sprintf("%s created successfully", friendlyName),
145+
Message: "Comment created successfully",
194146
Status: http.StatusOK,
195147
},
196148
Comment: discussion.Notes[0],

cmd/comment_helpers.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package main
2+
3+
import (
4+
"crypto/sha1"
5+
"fmt"
6+
7+
"github.com/xanzy/go-gitlab"
8+
)
9+
10+
/* LinePosition represents a position in a line range. Unlike the Gitlab struct, this does not contain LineCode with a sha1 of the filename */
11+
type LinePosition struct {
12+
Type string `json:"type"`
13+
OldLine int `json:"old_line"`
14+
NewLine int `json:"new_line"`
15+
}
16+
17+
/* LineRange represents the range of a note. */
18+
type LineRange struct {
19+
StartRange *LinePosition `json:"start"`
20+
EndRange *LinePosition `json:"end"`
21+
}
22+
23+
/* PositionData represents the position of a comment or note (relative to a file diff) */
24+
type PositionData struct {
25+
FileName string `json:"file_name"`
26+
NewLine *int `json:"new_line,omitempty"`
27+
OldLine *int `json:"old_line,omitempty"`
28+
HeadCommitSHA string `json:"head_commit_sha"`
29+
BaseCommitSHA string `json:"base_commit_sha"`
30+
StartCommitSHA string `json:"start_commit_sha"`
31+
Type string `json:"type"`
32+
LineRange *LineRange `json:"line_range,omitempty"`
33+
}
34+
35+
/* RequestWithPosition is an interface that abstracts the handling of position data for a comment or a draft comment */
36+
type RequestWithPosition interface {
37+
GetPositionData() PositionData
38+
}
39+
40+
/* buildCommentPosition takes a comment or draft comment request and builds the position data necessary for a location-based comment */
41+
func buildCommentPosition(commentWithPositionData RequestWithPosition) *gitlab.PositionOptions {
42+
positionData := commentWithPositionData.GetPositionData()
43+
44+
opt := &gitlab.PositionOptions{
45+
PositionType: &positionData.Type,
46+
StartSHA: &positionData.StartCommitSHA,
47+
HeadSHA: &positionData.HeadCommitSHA,
48+
BaseSHA: &positionData.BaseCommitSHA,
49+
NewPath: &positionData.FileName,
50+
OldPath: &positionData.FileName,
51+
NewLine: positionData.NewLine,
52+
OldLine: positionData.OldLine,
53+
}
54+
55+
if positionData.LineRange != nil {
56+
shaFormat := "%x_%d_%d"
57+
startFilenameSha := fmt.Sprintf(
58+
shaFormat,
59+
sha1.Sum([]byte(positionData.FileName)),
60+
positionData.LineRange.StartRange.OldLine,
61+
positionData.LineRange.StartRange.NewLine,
62+
)
63+
endFilenameSha := fmt.Sprintf(
64+
shaFormat,
65+
sha1.Sum([]byte(positionData.FileName)),
66+
positionData.LineRange.EndRange.OldLine,
67+
positionData.LineRange.EndRange.NewLine,
68+
)
69+
opt.LineRange = &gitlab.LineRangeOptions{
70+
Start: &gitlab.LinePositionOptions{
71+
Type: &positionData.LineRange.StartRange.Type,
72+
LineCode: &startFilenameSha,
73+
},
74+
End: &gitlab.LinePositionOptions{
75+
Type: &positionData.LineRange.EndRange.Type,
76+
LineCode: &endFilenameSha,
77+
},
78+
}
79+
}
80+
81+
return opt
82+
}

0 commit comments

Comments
 (0)