blob: 89871c0ed37f146a2f87dd4791b754f92773ded4 [file] [log] [blame]
;; -*- lexical-binding: t -*-
;; copyright 2020 the chromium os authors. all rights reserved.
;; use of this source code is governed by a bsd-style license that can be
;; found in the license file.
(require 'magit)
(require 'magit-section)
(require 'gerrit)
(defface gerrit-filepath
`((t :inherit magit-branch-remote))
"filepath")
(defconst gerrit-buffer-name "gerrit-comments"
"The name of the buffer where the gerrit summary is placed.")
(defconst gerrit-section-mode-map magit-section-mode-map
"Mode map for Gerrit Summary - copies standard magit map")
(defconst gerrit-section-type (gensym)
"Magit Sections need a symbol for a section type.
We don't currently use this functionality.")
(define-derived-mode gerrit-section-mode magit-section-mode
"Gerrit-Repo"
"Mode for displaying Gerrit comments within Emacs."
(when (fboundp 'evil-set-initial-state)
;; Evil Mode doesn't always play nice with the keymaps.
(evil-set-initial-state 'gerrit-section-mode 'emacs)))
(defun gerrit-comments ()
"Display buffer that shows comments for recent open changes.
This comment is idempotent."
(interactive)
(when (get-buffer gerrit-buffer-name)
(kill-buffer gerrit-buffer-name))
(gerrit-init)
(save-excursion
(set-buffer (get-buffer-create gerrit-buffer-name))
(magit-insert-section
(root)
(magit-insert-heading "Gerrit Comments\n\n")
(loop for change in
(hash-table-keys gerrit--change-to-filepath-comments) do
(unless (hash-table-empty-p
(gethash change gerrit--change-to-filepath-comments))
(magit-insert-section (file)
(magit-insert-heading
(format "%s - %s"
(gethash "subject" change)
(gethash "change_id" change)))
(loop for filepath in
(hash-table-keys
(gethash
change
gerrit--change-to-filepath-comments))
do
(gerrit--insert-section-comments change
filepath))))))
(when (hash-table-empty-p gerrit--change-to-filepath-comments)
(insert "No open changes!"))
(goto-char (point-min))
(gerrit-section-mode)
(pop-to-buffer gerrit-buffer-name)
(setf word-wrap t)
(setf truncate-lines nil)))
(define-button-type 'gerrit--filepath
'face 'gerrit-filepath)
(defun gerrit--navigate-to-comment (project-branch-pair
line
filepath-from-project-root
section-symbol)
"Navigates the user to a comment."
;; Open a closed section for a user to refer back to after click.
(when (oref section-symbol hidden)
(magit-section-toggle section-symbol))
(switch-to-buffer-other-window
(find-file-noselect (gerrit--get-abs-path-to-file
filepath-from-project-root
project-branch-pair
gerrit-repo-root)))
(goto-char (point-min))
(beginning-of-line line))
(defun gerrit--insert-comment-header (change
filepath-from-project-root
line
author
section-symbol)
"Inserts section header for a comment in the summary buffer."
(let (header-text
button-p
(begin-pos (point)))
(cond ((equal "/PATCHSET_LEVEL" filepath-from-project-root)
(setf header-text
(propertize
(format
"Patch Comment - %s"
author)
'face 'gerrit-filepath)))
((equal "/COMMIT_MSG" filepath-from-project-root)
;; TODO future CL for navigating
;; to commit message lines.
(setf header-text
(propertize
(format "Commit Message:%s - %s\n"
line
author)
'face 'gerrit-filepath)))
((equal "MERGE_LIST" filepath-from-project-root)
(message "There are merge list comments which we don't support"))
;; Default here are filepath comments
(t (progn
(setf header-text
(propertize
(format "%s:%s - %s\n"
filepath-from-project-root
line
author)
'face 'gerrit-filepath))
(setf button-p t))))
(magit-insert-heading header-text)
(when button-p
(make-button begin-pos
(point)
'type 'gerrit--filepath
'action
(lambda (button)
(gerrit--navigate-to-comment
change
line
filepath-from-project-root
section-symbol))))))
(defun gerrit--insert-section-comments (change
filepath-from-project-root)
"Inserts the comments for a given change and filepath."
;; We want the smaller lines first.
(sort (gethash filepath-from-project-root
(gethash change gerrit--change-to-filepath-comments))
(lambda (a b)
;; If comment has no line, we don't care about ordering.
(< (or (gethash "line" a) 0)
(or (gethash "line" b) 0))))
(loop for comment-info across
(gethash filepath-from-project-root
(gethash change gerrit--change-to-filepath-comments))
do
(let ((section-symbol (gensym))
(line (gethash "line" comment-info))
(begin-pos (point)))
(magit-insert-section
;; We use section symbols to toggle when navigating.
section-symbol
(gerrit-section-type nil t)
(gerrit--insert-comment-header
change
filepath-from-project-root
line
(gethash "name" (gethash "author" comment-info))
section-symbol)
(gerrit--section-insert-comment change filepath-from-project-root comment-info)))))
(defun gerrit--section-insert-comment (change filepath-from-project-root comment-info)
"Inserts the author, link to, and body of a comment."
(magit-insert-section-body
(insert
(format "Author: %s\nLink: %s\nComment: %s\n"
(format
"%s - %s"
(gethash "name" (gethash "author" comment-info))
(gethash "email" (gethash "author" comment-info)))
;; TODO buttonize link.
(propertize
(format
"https://%s/c/%s/+/%s/%s/%s#%s"
(gethash (gethash "change_id" change) gerrit--change-to-host)
(url-hexify-string (gethash "project" change))
(gethash "_number" change)
;; Default is nil if comments only on a single patch.
(or (gethash "patch_set" comment-info) "1")
(url-hexify-string filepath-from-project-root)
;; Here we default to going to the top of the file.
;; if there is no line with a comment.
(or (gethash "line" comment-info) "1"))
'face 'link)
(gethash "message" comment-info)))))
(provide 'gerrit-section)
(require 'gerrit-section)