~niklaseklund/egerrit

ref: 1ed03351b22f8b47fa0a4e538d059bc85eff7631 egerrit/egerrit-review.el -rw-r--r-- 9.8 KiB
1ed03351Niklas Eklund Increase possibility to detect autogenerated files 3 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
;;; egerrit-review.el --- Review for egerrit -*- lexical-binding: t; -*-

;; Copyright (C) 2022  Free Software Foundation, Inc.

;; This file is not part of GNU Emacs.

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; This library provides the review for `egerrit'.

;;; Code:

;;;; Requirements

(require 'egerrit-core)
(require 'egerrit-database)
(require 'egerrit-datastructures)
(require 'egerrit-diff)
(require 'egerrit-git)
(require 'egerrit-request)

(declare-function egerrit-get-detailed-change "egerrit")
(declare-function egerrit-select-revision "egerrit")

(defvar egerrit--mode)
(defvar egerrit--switch-branch)
(defvar egerrit--current-change)
(defvar egerrit-user-name)

;;;; Functions

(defun egerrit-post-review-label (change revision label &optional review-message)
  "Post LABEL on CHANGE's REVISION.

Optionally send a REVIEW-MESSAGE."
  (let* ((url (egerrit-request-url
               (format "/changes/%s/revisions/%s/review"
                       (egerrit-change-id change)
                       (egerrit-revision-id revision))))
         (review `(("labels" . ((,(car label) . ,(cdr label)))))))
    (when review-message
      (push `("message" . ,review-message) review))
    (egerrit-request
     url
     :type "POST"
     :header '(("Content-Type" . "application/json"))
     :auth t
     :data review)))

(defun egerrit-review-groups ()
  "Return a list of all review groups."
  (let* ((url (egerrit-request-url "/groups/"))
         (response
          (egerrit-request
           url
           :auth t
           :type "GET"
           :header '(("Content-Type" . "application/json"))
           :parser #'egerrit-request-parser)))
    (thread-last response
                 (seq-map #'car)
                 (seq-map #'symbol-name))))

;;;; Commands

;;;###autoload
(defun egerrit-author-review-change (change)
  "Review CHANGE as an author."
  (interactive
   (list (tabulated-list-get-id)))
  (let ((egerrit--switch-branch nil)
        (egerrit--mode 'author))
    (egerrit-review-change change)))

;;;###autoload
(defun egerrit-author-review-partial-change (change)
  "Review a partial CHANGE as an author."
  (interactive
   (list (tabulated-list-get-id)))
  (let ((egerrit--switch-branch nil)
        (egerrit--mode 'author))
    (egerrit-review-partial-change change)))

;;;###autoload
(defun egerrit-review-change (change)
  "Review CHANGE."
  (interactive
   (list (tabulated-list-get-id)))
  (when (egerrit-change-p change)
    (let* ((egerrit--mode (or egerrit--mode 'reviewer))
           (egerrit--current-change (egerrit-get-detailed-change change))
           (revision
            (if current-prefix-arg
                (egerrit-select-revision egerrit--current-change)
              (seq-first (seq-reverse (egerrit--change-revisions egerrit--current-change))))))
      (if (and (eq egerrit--mode 'reviewer)
               (not (egerrit--clean-git-repository change)))
          (message "The git repository can't be dirty when engaging in a review")
        (egerrit--open-revision-diff revision egerrit--current-change)))))

;;;###autoload
(defun egerrit-review-partial-change (change)
  "Review a partial CHANGE."
  (interactive
   (list (tabulated-list-get-id)))
  (when (egerrit-change-p change)
    (let* ((egerrit--mode (or egerrit--mode 'reviewer))
           (egerrit--current-change (egerrit-get-detailed-change change))
           (base-revision
            (egerrit-select-revision egerrit--current-change (egerrit--change-revisions egerrit--current-change) "Select base revision: "))
           (revision
            (egerrit-select-revision egerrit--current-change
                                     (seq-filter (lambda (it)
                                                   (> (egerrit--revision-number it) (egerrit--revision-number base-revision)))
                                                 (egerrit--change-revisions egerrit--current-change)))))
      (if (and (eq egerrit--mode 'reviewer)
               (not (egerrit--clean-git-repository change)))
          (message "The git repository can't be dirty when engaging in a review")
        (egerrit--open-partial-revision-diff base-revision revision egerrit--current-change)))))

;;;###autoload
(defun egerrit-publish-review-label (&optional message)
  "Set and publish a review label for CHANGE.

Optionally add a MESSAGE."
  (interactive "P")
  (when-let* ((change (cond ((eq major-mode 'egerrit-dashboard-mode) (tabulated-list-get-id))
                            ((eq major-mode 'egerrit-conversation-mode) egerrit--current-change)
                            ((eq major-mode 'egerrit-diff-mode) egerrit--current-change)
                            (t nil)))
              (detailed-change (egerrit-get-detailed-change change))
              (revision (seq-first (seq-reverse (egerrit--change-revisions detailed-change))))
              (candidates (egerrit--review-label-candidates detailed-change))
              (metadata `(metadata
                          (cycle-sort-function . identity)
                          (display-sort-function . identity)))
              (collection (lambda (string predicate action)
                            (if (eq action 'metadata)
                                metadata
                              (complete-with-action action candidates string predicate))))
              (review (completing-read "Select: " collection nil t)))

    ;; Send review to Gerrit
    (egerrit-post-review-label
     detailed-change revision
     (cdr (assoc review candidates))
     (when message
       (read-string "Enter message: ")))

    ;; Store review in database
    (egerrit--update-review-db
     change
     revision
     (alist-get review candidates nil nil #'string=))

    (message "Published review: %s" (car (assoc review candidates)))))

;;;; Support functions

(defun egerrit--review-label-candidates (change)
  "Return possible review labels for CHANGE."
  (let* ((labels (egerrit--change-labels change))
         (code-review-values (let-alist labels .Code-Review.values))
         (secondary-review-values (let-alist labels .Secondary-Review.values)))

    (when egerrit-user-name
      ;; Code-Review
      (when-let ((user-settings (seq-find (lambda (it)
                                            (string= (alist-get 'username it) egerrit-user-name))
                                          (let-alist labels .Code-Review.all)))
                 (range (let-alist user-settings .permitted_voting_range)))
        ;; Filter code review values
        (setq code-review-values
              (thread-last code-review-values
                           (seq-filter (lambda (it)
                                         (let ((value (string-to-number (symbol-name (car it)))))
                                           (and (<= value (let-alist range .max))
                                                (>= value (let-alist range .min)))))))))

      ;; Secondary-Review
      (when-let ((user-settings (seq-find (lambda (it)
                                            (string= (alist-get 'username it) egerrit-user-name))
                                          (let-alist labels .Secondary-Review.all)))
                 (range (let-alist user-settings .permitted_voting_range)))
        ;; Filter secondary review values
        (setq secondary-review-values
              (thread-last secondary-review-values
                           (seq-filter (lambda (it)
                                         (let ((value (string-to-number (symbol-name (car it)))))
                                           (and (<= value (let-alist range .max))
                                                (>= value (let-alist range .min))))))))))

    ;; Create a list of options
    `(,@(seq-map (lambda (it)
                   (let ((value (symbol-name (car it)))
                         (description (cdr it)))
                     (put-text-property 0 (length value)
                                        'face
                                        (cond ((> (string-to-number value) 0) 'egerrit-inv-success-face)
                                              ((< (string-to-number value) 0) 'egerrit-inv-error-face)
                                              (t 'egerrit-unknown-face))
                                        value)
                     (put-text-property 0 (length description) 'face 'egerrit-unknown-face description)
                     `(,(format "SR: %s %s" value description) . ("Secondary-Review" . ,value))))
                 secondary-review-values)
      ,@(seq-map (lambda (it)
                   (let ((value (symbol-name (car it)))
                         (description (cdr it)))
                     (put-text-property 0 (length value)
                                        'face
                                        (cond ((> (string-to-number value) 0) 'egerrit-inv-success-face)
                                              ((< (string-to-number value) 0) 'egerrit-inv-error-face)
                                              (t 'egerrit-unknown-face))
                                        value)
                     (put-text-property 0 (length description) 'face 'egerrit-unknown-face description)

                     `(,(format "CR: %s %s" value  description) . ("Code-Review" . ,value))))
                 code-review-values))))

(provide 'egerrit-review)

;;; egerrit-review.el ends here