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
|
;;; TODO(plundblad):
;;; - Allow adding comments outside of the diff directly from a target
;;; file.
;;; - Hide hunks with only imports etc.
;;; - Hide whitespace-only changes.
;;; - Fix path depotization to not assume google3 or eng.
;;; This elisp file adds a mode based on diff-mode to add comments to specific
;;; lines of files in the diff. It is intended to be used with files produced
;;; by g4 diff (it requires the target files to exist). In a diff file,
;;; enable this mode with crq-mode. You can also run g4 diff
;;; and enable crq mode with the command crq-review.
;;; Then press Enter on any line in the diff
;;; to open up a comment at the end of the file annotated with filename and
;;; line number.
;;;
;;; When done, enter any overall comments right below the "=== COMMENTS ==="
;;; line. The first line can be "LGTM" or "FYI" to add a vote. Then
;;; use the command crq-publish to upload and publish your comments to
;;; critique.
(require 'diff-mode)
(require 'p4-google)
(defconst crq-comments-header "=== COMMENTS ===\n")
(defconst crq-file-separator
"========================================================================\n")
(defconst crq-file-format
(concat crq-file-separator "File %s\n"))
(defconst crq-line-format
"------------------------------------\nLine %d: %s\n\n")
(defconst mch-program
(expand-file-name "mph.py"
(file-name-directory load-file-name)))
(defconst download-program
(expand-file-name "download_issue"
(file-name-directory load-file-name)))
(define-derived-mode crq-mode diff-mode "Crq"
"Special diff mode for code reviews
\\{crq-mode-map}"
(crq-ensure-header)
(crq-install-diff-map))
(define-key crq-mode-map "\C-c\C-p" 'crq-publish)
(defun crq-review (change-list-number)
"Run download script asynchronously on CHANGE-LIST-NUMBER (a string) and
activate crq-mode in the resulting buffer."
(interactive (list (read-change-list)))
(let ((process
(start-process "Gerrit Review" "gerrit-review" download-program change-list-number)))
(set-process-sentinel process 'crq-diff-sentinel)
(message "Gerrit download...")))
; TODO(plundblad): Perhaps run the python script asynchronously.
(defun crq-publish (arg)
"Publishes the comments in the current buffer to critique."
(interactive "P")
(goto-char (point-max))
(let ((result (call-process-region (point-min) (point-max) mch-program nil
(current-buffer) t
"/dev/stdin")))
(if (not (zerop result))
(progn
(message "Error running mch"))))
t)
(defun crq-comment ()
(interactive)
(let* ((loc (diff-find-source-location nil t))
(buf (nth 0 loc))
(pos (nth 2 loc))
(src (nth 3 loc))
(offset (+ (car pos) (cdr src)))
(line
(with-current-buffer buf
(save-excursion
(goto-char offset)
(thing-at-point 'line))))
(filename (buffer-file-name buf))
(lineno
(with-current-buffer buf
(save-excursion
(goto-char offset)
; count-lines counts the current line if point is not
; at the beginning of the line.
(beginning-of-line)
(1+ (count-lines (point-min) (point)))))))
(crq-ensure-header)
(crq-add-comment
(file-relative-name filename) lineno line)))
(defun crq-ensure-header ()
(save-excursion
(goto-char (point-min))
(if (not (search-forward crq-comments-header nil t))
(progn
(goto-char (point-max))
(insert ?\n crq-comments-header
?\n crq-file-separator)))))
(defvar crq-diff-map
(let ((map (make-sparse-keymap)))
(define-key map "\r" 'crq-comment)
map))
(defun crq-install-diff-map ()
(let* ((end
(save-excursion
(goto-char (point-max))
(unless (search-backward crq-comments-header nil t)
(error "No comment header"))
(point)))
(overlay (make-overlay (point-min) end)))
(overlay-put overlay 'keymap crq-diff-map)))
(defun crq-fix-filename (fullname)
(cond
((string-match "/\\(google3\\|eng\\)/.*$" fullname)
(concat "//depot" (match-string 0 fullname)))
((string-match "/\\(a\\|b\\)/.*$" fullname)
(substring (match-string 0 fullname) 3))
(t fullname)))
(defun crq-trim-line (line)
(if (string-match "^\\s-*\\(.*?\\)\\s-*$" line)
(match-string 1 line)
line))
(defun crq-add-comment (filename lineno text)
(push-mark)
(goto-char (point-max))
(forward-line -1)
(beginning-of-line)
(unless (looking-at crq-file-separator)
(error "Garbage at the end of the file"))
(insert (format crq-file-format filename))
(insert (format crq-line-format lineno
(crq-trim-line text)))
(forward-line -1))
(defun crq-diff-sentinel (process event)
(if (string= event "finished\n")
(progn
(message "Downloaded from gerrit")
(pop-to-buffer (process-buffer process))
(crq-mode)
(goto-char (point-min))
(search-forward "\t" nil t))
(progn
(message "g4 diff %s" event)
(ding))))
(provide 'crq)
|