From c7693191d45cc931d8c70e56977be6f4bf3b3c0c Mon Sep 17 00:00:00 2001 From: Lucas Sta Maria Date: Sat, 23 Aug 2025 00:31:04 +0800 Subject: [PATCH] feat: add `ghpr-review-mode` --- ghpr-api.el | 20 +++++++++++++++ ghpr-review.el | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/ghpr-api.el b/ghpr-api.el index 7f67a03..41c9acf 100644 --- a/ghpr-api.el +++ b/ghpr-api.el @@ -75,6 +75,26 @@ Returns a list of pull request objects on success, nil on failure." (message "Error fetching pull requests: %s" error-thrown))))) result)) +(defun ghpr--get-patch-content (repo-name pr-number) + "Retrieve the diff contents for PR-NUMBER in REPO-NAME." + (let ((token (ghpr--get-token)) + (url (format "https://api.github.com/repos/%s/pulls/%s" repo-name pr-number)) + (result nil)) + (when token + (request url + :type "GET" + :headers `(("Accept" . "application/vnd.github.diff") + ("Authorization" . ,(format "Bearer %s" token)) + ("X-GitHub-Api-Version" . "2022-11-28")) + :sync t + :success (cl-function + (lambda (&key data &allow-other-keys) + (setq result data))) + :error (cl-function + (lambda (&key error-thrown &allow-other-keys) + (message "Error fetching diff: %s" error-thrown))))) + result)) + (provide 'ghpr-api) ;;; ghpr-api.el ends here diff --git a/ghpr-review.el b/ghpr-review.el index 8ef9608..169a06e 100644 --- a/ghpr-review.el +++ b/ghpr-review.el @@ -31,6 +31,76 @@ ;;; Code: +(require 'request) + +(require 'ghpr-api) +(require 'ghpr-utils) + +(defface ghpr-review-added-line + '((t (:background "green" :foreground "black"))) + "Face for added lines in PR diff." + :group 'ghpr) + +(defface ghpr-review-removed-line + '((t (:background "red" :foreground "black"))) + "Face for removed lines in PR diff." + :group 'ghpr) + +(defvar ghpr-review-font-lock-keywords + '(("^\\(> \\+[^+]\\)\\(.*\\)$" + (0 'ghpr-review-added-line t)) + ("^\\(> \\+\\)$" ; Handle line with just "> +" + (0 'ghpr-review-added-line t)) + ("^\\(> -[^-]\\)\\(.*\\)$" + (0 'ghpr-review-removed-line t)) + ("^\\(> -\\)$" ; Handle line with just "> -" + (0 'ghpr-review-removed-line t))) + "Font lock keywords for ghpr-review-mode.") + +(define-derived-mode ghpr-review-mode special-mode "GHPR Review" + "Major mode for reviewing GitHub pull requests." + :group 'ghpr + (setq truncate-lines t) + (setq font-lock-defaults '(ghpr-review-font-lock-keywords t nil nil nil)) + (font-lock-ensure)) +(defun ghpr--prefix-line (line) + "Prefix the LINE with `> '." + (concat "> " line)) + +(defun ghpr--prefix-lines (text) + "Prefix every line in TEXT with `> '." + (let ((lines (split-string text "\n"))) + (mapconcat #'ghpr--prefix-line lines "\n"))) + +(defun ghpr--open-pr/collect-contents (pr repo-name) + "Return a string of the title, body, and patch content separated by newlines." + (let* ((body (or (alist-get 'body pr) "")) + (number (alist-get 'number pr)) + (content-parts (list (ghpr--pr-summary pr)))) + (when (not (string-empty-p body)) + (push body content-parts)) + (let ((patch-content (ghpr--get-patch-content repo-name number))) + (when patch-content + (push patch-content content-parts))) + (mapconcat 'identity (reverse content-parts) "\n\n"))) + +(defun ghpr--open-pr/insert-contents (pr repo-name) + "Insert the contents of the pr into the current buffer." + (erase-buffer) + (let ((contents (ghpr--open-pr/collect-contents pr repo-name))) + (insert (ghpr--prefix-lines contents)))) + +(defun ghpr--open-pr (pr repo-name) + "Open a new buffer containing the body of the PR." + (let* ((number (alist-get 'number pr)) + (buffer-name (format "*ghpr-pr-%s*" number)) + (buffer (get-buffer-create buffer-name))) + (with-current-buffer buffer + (ghpr-review-mode) + (ghpr--open-pr/insert-contents pr repo-name) + (goto-char (point-min))) + (display-buffer buffer))) + (provide 'ghpr-review) ;;; ghpr-review.el ends here