Skip to content

Commit 9f898aa

Browse files
harrisoncramerjakubbortlikashish10alex
authored
Bug Fixes (#470)
* fix: Restore buffer local settings outside reviewer (#446) * fix: do not show healthcheck alert for warnings (#468) * feat: Add MR URL to the summary details (#467) * fix: make cycling reviewed files faster (#474) * feat(pipeline): display trigger jobs for a pipeline in the pipelines popup (#465) * fix: Jumping to renamed files (#484) --------- Co-authored-by: Jakub F. Bortlík <[email protected]> Co-authored-by: Ashish Alex <[email protected]>
1 parent 3b396a5 commit 9f898aa

File tree

23 files changed

+522
-357
lines changed

23 files changed

+522
-357
lines changed

.github/CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ It's possible that the feature you want is already implemented, or does not belo
1010

1111
If you are using Lazy as a plugin manager, the easiest way to work on changes is by setting a specific path for the plugin that points to your repository locally. This is what I do:
1212

13-
```lua
13+
```lua
1414
{
1515
"harrisoncramer/gitlab.nvim",
1616
dependencies = {
@@ -54,8 +54,8 @@ $ luacheck --globals vim busted --no-max-line-length -- .
5454

5555
4. Make the merge request to the `develop` branch of `.gitlab.nvim`
5656

57-
Please provide a description of the feature, and links to any relevant issues.
57+
Please provide a description of the feature, and links to any relevant issues.
5858

59-
That's it! I'll try to respond to any incoming merge request in a few days. Once we've reviewed it, it will be merged into the develop branch.
59+
That's it! I'll try to respond to any incoming merge request in a few days. Once we've reviewed it, it will be merged into the develop branch.
6060

6161
After some time, if the develop branch is found to be stable, that branch will be merged into `main` and released. When merged into `main` the pipeline will detect whether we're merging in a patch, minor, or major change, and create a new tag (e.g. 1.0.12) and release.

cmd/app/comment_helpers.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,13 @@ type RequestWithPosition interface {
4242
func buildCommentPosition(commentWithPositionData RequestWithPosition) *gitlab.PositionOptions {
4343
positionData := commentWithPositionData.GetPositionData()
4444

45-
// If the file has been renamed, then this is a relevant part of the payload
46-
oldFileName := positionData.OldFileName
47-
if oldFileName == "" {
48-
oldFileName = positionData.FileName
49-
}
50-
5145
opt := &gitlab.PositionOptions{
5246
PositionType: &positionData.Type,
5347
StartSHA: &positionData.StartCommitSHA,
5448
HeadSHA: &positionData.HeadCommitSHA,
5549
BaseSHA: &positionData.BaseCommitSHA,
5650
NewPath: &positionData.FileName,
57-
OldPath: &oldFileName,
51+
OldPath: &positionData.OldFileName,
5852
NewLine: positionData.NewLine,
5953
OldLine: positionData.OldLine,
6054
}

cmd/app/pipeline.go

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,18 @@ type RetriggerPipelineResponse struct {
2020
type PipelineWithJobs struct {
2121
Jobs []*gitlab.Job `json:"jobs"`
2222
LatestPipeline *gitlab.PipelineInfo `json:"latest_pipeline"`
23+
Name string `json:"name"`
2324
}
2425

2526
type GetPipelineAndJobsResponse struct {
2627
SuccessResponse
27-
Pipeline PipelineWithJobs `json:"latest_pipeline"`
28+
Pipelines []PipelineWithJobs `json:"latest_pipeline"`
2829
}
2930

3031
type PipelineManager interface {
3132
ListProjectPipelines(pid interface{}, opt *gitlab.ListProjectPipelinesOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.PipelineInfo, *gitlab.Response, error)
3233
ListPipelineJobs(pid interface{}, pipelineID int, opts *gitlab.ListJobsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Job, *gitlab.Response, error)
34+
ListPipelineBridges(pid interface{}, pipelineID int, opts *gitlab.ListJobsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Bridge, *gitlab.Response, error)
3335
RetryPipelineBuild(pid interface{}, pipeline int, options ...gitlab.RequestOptionFunc) (*gitlab.Pipeline, *gitlab.Response, error)
3436
}
3537

@@ -101,7 +103,6 @@ func (a pipelineService) GetPipelineAndJobs(w http.ResponseWriter, r *http.Reque
101103
}
102104

103105
jobs, res, err := a.client.ListPipelineJobs(a.projectInfo.ProjectId, pipeline.ID, &gitlab.ListJobsOptions{})
104-
105106
if err != nil {
106107
handleError(w, err, "Could not get pipeline jobs", http.StatusInternalServerError)
107108
return
@@ -112,13 +113,51 @@ func (a pipelineService) GetPipelineAndJobs(w http.ResponseWriter, r *http.Reque
112113
return
113114
}
114115

116+
pipelines := []PipelineWithJobs{}
117+
pipelines = append(pipelines, PipelineWithJobs{
118+
Jobs: jobs,
119+
LatestPipeline: pipeline,
120+
Name: "root",
121+
})
122+
123+
bridges, res, err := a.client.ListPipelineBridges(a.projectInfo.ProjectId, pipeline.ID, &gitlab.ListJobsOptions{})
124+
125+
if err != nil {
126+
handleError(w, err, "Could not get pipeline trigger jobs", http.StatusInternalServerError)
127+
return
128+
}
129+
if res.StatusCode >= 300 {
130+
handleError(w, GenericError{r.URL.Path}, "Could not get pipeline trigger jobs", res.StatusCode)
131+
return
132+
}
133+
134+
for _, bridge := range bridges {
135+
if bridge.DownstreamPipeline == nil {
136+
continue
137+
}
138+
139+
pipelineIdInBridge := bridge.DownstreamPipeline.ID
140+
bridgePipelineJobs, res, err := a.client.ListPipelineJobs(bridge.DownstreamPipeline.ProjectID, pipelineIdInBridge, &gitlab.ListJobsOptions{})
141+
if err != nil {
142+
handleError(w, err, "Could not get jobs for a pipeline from a trigger job", http.StatusInternalServerError)
143+
return
144+
}
145+
if res.StatusCode >= 300 {
146+
handleError(w, GenericError{r.URL.Path}, "Could not get jobs for a pipeline from a trigger job", res.StatusCode)
147+
return
148+
}
149+
150+
pipelines = append(pipelines, PipelineWithJobs{
151+
Jobs: bridgePipelineJobs,
152+
LatestPipeline: bridge.DownstreamPipeline,
153+
Name: bridge.Name,
154+
})
155+
}
156+
115157
w.WriteHeader(http.StatusOK)
116158
response := GetPipelineAndJobsResponse{
117159
SuccessResponse: SuccessResponse{Message: "Pipeline retrieved"},
118-
Pipeline: PipelineWithJobs{
119-
LatestPipeline: pipeline,
120-
Jobs: jobs,
121-
},
160+
Pipelines: pipelines,
122161
}
123162

124163
err = json.NewEncoder(w).Encode(response)

cmd/app/pipeline_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ func (f fakePipelineManager) ListPipelineJobs(pid interface{}, pipelineID int, o
2727
return []*gitlab.Job{}, resp, err
2828
}
2929

30+
func (f fakePipelineManager) ListPipelineBridges(pid interface{}, pipelineID int, opts *gitlab.ListJobsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Bridge, *gitlab.Response, error) {
31+
resp, err := f.handleGitlabError()
32+
if err != nil {
33+
return nil, nil, err
34+
}
35+
return []*gitlab.Bridge{}, resp, err
36+
}
37+
3038
func (f fakePipelineManager) RetryPipelineBuild(pid interface{}, pipeline int, options ...gitlab.RequestOptionFunc) (*gitlab.Pipeline, *gitlab.Response, error) {
3139
resp, err := f.handleGitlabError()
3240
if err != nil {

doc/gitlab.nvim.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ you call this function with no values the defaults will be used:
302302
"delete_branch",
303303
"squash",
304304
"labels",
305+
"web_url",
305306
},
306307
},
307308
discussion_signs = {
@@ -315,6 +316,7 @@ you call this function with no values the defaults will be used:
315316
comment = "→|",
316317
range = " |",
317318
},
319+
skip_old_revision_discussion = false, -- Don't show diagnostics for discussions that were created for earlier MR revisions
318320
},
319321
pipeline = {
320322
created = "",

lua/gitlab/actions/comment.lua

Lines changed: 38 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ local reviewer = require("gitlab.reviewer")
1515
local Location = require("gitlab.reviewer.location")
1616

1717
local M = {
18-
current_win = nil,
1918
start_line = nil,
2019
end_line = nil,
2120
draft_popup = nil,
@@ -25,10 +24,9 @@ local M = {
2524
---Fires the API that sends the comment data to the Go server, called when you "confirm" creation
2625
---via the M.settings.keymaps.popup.perform_action keybinding
2726
---@param text string comment text
28-
---@param visual_range LineRange | nil range of visual selection or nil
2927
---@param unlinked boolean if true, the comment is not linked to a line
3028
---@param discussion_id string | nil The ID of the discussion to which the reply is responding, nil if not a reply
31-
local confirm_create_comment = function(text, visual_range, unlinked, discussion_id)
29+
local confirm_create_comment = function(text, unlinked, discussion_id)
3230
if text == nil then
3331
u.notify("Reviewer did not provide text of change", vim.log.levels.ERROR)
3432
return
@@ -75,30 +73,16 @@ local confirm_create_comment = function(text, visual_range, unlinked, discussion
7573
return
7674
end
7775

78-
local reviewer_data = reviewer.get_reviewer_data()
79-
if reviewer_data == nil then
80-
u.notify("Error getting reviewer data", vim.log.levels.ERROR)
81-
return
82-
end
83-
84-
local location = Location.new(reviewer_data, visual_range)
85-
location:build_location_data()
86-
local location_data = location.location_data
87-
if location_data == nil then
88-
u.notify("Error getting location information", vim.log.levels.ERROR)
89-
return
90-
end
91-
9276
local revision = state.MR_REVISIONS[1]
9377
local position_data = {
94-
file_name = reviewer_data.file_name,
95-
old_file_name = reviewer_data.old_file_name,
78+
file_name = M.location.reviewer_data.file_name,
79+
old_file_name = M.location.reviewer_data.old_file_name,
9680
base_commit_sha = revision.base_commit_sha,
9781
start_commit_sha = revision.start_commit_sha,
9882
head_commit_sha = revision.head_commit_sha,
99-
old_line = location_data.old_line,
100-
new_line = location_data.new_line,
101-
line_range = location_data.line_range,
83+
old_line = M.location.location_data.old_line,
84+
new_line = M.location.location_data.new_line,
85+
line_range = M.location.location_data.line_range,
10286
}
10387

10488
-- Creating a new comment (linked to specific changes)
@@ -148,10 +132,10 @@ M.confirm_edit_comment = function(discussion_id, note_id, unlinked)
148132
end
149133

150134
---@class LayoutOpts
151-
---@field ranged boolean
152135
---@field unlinked boolean
153136
---@field discussion_id string|nil
154137
---@field reply boolean|nil
138+
---@field file_name string|nil
155139

156140
---This function sets up the layout and popups needed to create a comment, note and
157141
---multi-line comment. It also sets up the basic keybindings for switching between
@@ -163,21 +147,24 @@ M.create_comment_layout = function(opts)
163147
local title
164148
local user_settings
165149
if opts.discussion_id ~= nil then
166-
title = "Reply"
150+
title = "Reply" .. (opts.file_name and string.format(" [%s]", opts.file_name) or "")
167151
user_settings = popup_settings.reply
168152
elseif opts.unlinked then
169153
title = "Note"
170154
user_settings = popup_settings.note
171155
else
172-
title = "Comment"
156+
-- TODO: investigate why `old_file_name` is in fact the new name for renamed files!
157+
local file_name = M.location.reviewer_data.old_file_name ~= "" and M.location.reviewer_data.old_file_name
158+
or M.location.reviewer_data.file_name
159+
title =
160+
popup.create_title("Comment", file_name, M.location.visual_range.start_line, M.location.visual_range.end_line)
173161
user_settings = popup_settings.comment
174162
end
175163
local settings = u.merge(popup_settings, user_settings or {})
176164

177-
M.current_win = vim.api.nvim_get_current_win()
165+
local current_win = vim.api.nvim_get_current_win()
178166
M.comment_popup = Popup(popup.create_popup_state(title, settings))
179167
M.draft_popup = Popup(popup.create_box_popup_state("Draft", false, settings))
180-
M.start_line, M.end_line = u.get_visual_selection_boundaries()
181168

182169
local internal_layout = Layout.Box({
183170
Layout.Box(M.comment_popup, { grow = 1 }),
@@ -194,22 +181,21 @@ M.create_comment_layout = function(opts)
194181
}, internal_layout)
195182

196183
popup.set_cycle_popups_keymaps({ M.comment_popup, M.draft_popup })
197-
popup.set_up_autocommands(M.comment_popup, layout, M.current_win)
184+
popup.set_up_autocommands(M.comment_popup, layout, current_win)
198185

199-
local range = opts.ranged and { start_line = M.start_line, end_line = M.end_line } or nil
200186
local unlinked = opts.unlinked or false
201187

202188
---Keybinding for focus on draft section
203189
popup.set_popup_keymaps(M.draft_popup, function()
204190
local text = u.get_buffer_text(M.comment_popup.bufnr)
205-
confirm_create_comment(text, range, unlinked, opts.discussion_id)
206-
vim.api.nvim_set_current_win(M.current_win)
191+
confirm_create_comment(text, unlinked, opts.discussion_id)
192+
vim.api.nvim_set_current_win(current_win)
207193
end, miscellaneous.toggle_bool, popup.non_editable_popup_opts)
208194

209195
---Keybinding for focus on text section
210196
popup.set_popup_keymaps(M.comment_popup, function(text)
211-
confirm_create_comment(text, range, unlinked, opts.discussion_id)
212-
vim.api.nvim_set_current_win(M.current_win)
197+
confirm_create_comment(text, unlinked, opts.discussion_id)
198+
vim.api.nvim_set_current_win(current_win)
213199
end, miscellaneous.attach_file, popup.editable_popup_opts)
214200

215201
vim.schedule(function()
@@ -223,44 +209,43 @@ end
223209
--- This function will open a comment popup in order to create a comment on the changed/updated
224210
--- line in the current MR
225211
M.create_comment = function()
212+
M.location = Location.new()
226213
if not M.can_create_comment(false) then
227214
return
228215
end
229216

230-
local layout = M.create_comment_layout({ ranged = false, unlinked = false })
217+
local layout = M.create_comment_layout({ unlinked = false })
231218
layout:mount()
232219
end
233220

234221
--- This function will open a multi-line comment popup in order to create a multi-line comment
235222
--- on the changed/updated line in the current MR
236223
M.create_multiline_comment = function()
224+
M.location = Location.new()
237225
if not M.can_create_comment(true) then
238226
u.press_escape()
239227
return
240228
end
241229

242-
local layout = M.create_comment_layout({ ranged = true, unlinked = false })
230+
local layout = M.create_comment_layout({ unlinked = false })
243231
layout:mount()
244232
end
245233

246234
--- This function will open a a popup to create a "note" (e.g. unlinked comment)
247235
--- on the changed/updated line in the current MR
248236
M.create_note = function()
249-
local layout = M.create_comment_layout({ ranged = false, unlinked = true })
237+
local layout = M.create_comment_layout({ unlinked = true })
250238
layout:mount()
251239
end
252240

253241
---Given the current visually selected area of text, builds text to fill in the
254242
---comment popup with a suggested change
255243
---@return LineRange|nil
256-
---@return integer
257244
local build_suggestion = function()
258245
local current_line = vim.api.nvim_win_get_cursor(0)[1]
259-
M.start_line, M.end_line = u.get_visual_selection_boundaries()
260-
261-
local range_length = M.end_line - M.start_line
246+
local range_length = M.location.visual_range.end_line - M.location.visual_range.start_line
262247
local backticks = "```"
263-
local selected_lines = u.get_lines(M.start_line, M.end_line)
248+
local selected_lines = u.get_lines(M.location.visual_range.start_line, M.location.visual_range.end_line)
264249

265250
for _, line in ipairs(selected_lines) do
266251
if string.match(line, "^```%S*$") then
@@ -270,36 +255,37 @@ local build_suggestion = function()
270255
end
271256

272257
local suggestion_start
273-
if M.start_line == current_line then
258+
if M.location.visual_range.start_line == current_line then
274259
suggestion_start = backticks .. "suggestion:-0+" .. range_length
275-
elseif M.end_line == current_line then
260+
elseif M.location.visual_range.end_line == current_line then
276261
suggestion_start = backticks .. "suggestion:-" .. range_length .. "+0"
277262
else
278263
--- This should never happen afaik
279264
u.notify("Unexpected suggestion position", vim.log.levels.ERROR)
280-
return nil, 0
265+
return nil
281266
end
282267
suggestion_start = suggestion_start
283268
local suggestion_lines = {}
284269
table.insert(suggestion_lines, suggestion_start)
285270
vim.list_extend(suggestion_lines, selected_lines)
286271
table.insert(suggestion_lines, backticks)
287272

288-
return suggestion_lines, range_length
273+
return suggestion_lines
289274
end
290275

291276
--- This function will open a a popup to create a suggestion comment
292277
--- on the changed/updated line in the current MR
293278
--- See: https://docs.gitlab.com/ee/user/project/merge_requests/reviews/suggestions.html
294279
M.create_comment_suggestion = function()
280+
M.location = Location.new()
295281
if not M.can_create_comment(true) then
296282
u.press_escape()
297283
return
298284
end
299285

300-
local suggestion_lines, range_length = build_suggestion()
286+
local suggestion_lines = build_suggestion()
301287

302-
local layout = M.create_comment_layout({ ranged = range_length > 0, unlinked = false })
288+
local layout = M.create_comment_layout({ unlinked = false })
303289
layout:mount()
304290

305291
vim.schedule(function()
@@ -368,6 +354,11 @@ M.can_create_comment = function(must_be_visual)
368354
return false
369355
end
370356

357+
if M.location == nil or M.location.location_data == nil then
358+
u.notify("Error getting location information", vim.log.levels.ERROR)
359+
return false
360+
end
361+
371362
return true
372363
end
373364

0 commit comments

Comments
 (0)