From a265292b9e63daf07f4e36670df91c89043d2676 Mon Sep 17 00:00:00 2001 From: Lucas Sta Maria Date: Wed, 27 Aug 2025 16:32:35 +0800 Subject: [PATCH 1/4] feat: add `ghpr-open-pr` command --- ghpr-api.el | 22 ++++++++++++++++++++++ ghpr.el | 9 +++++++++ 2 files changed, 31 insertions(+) diff --git a/ghpr-api.el b/ghpr-api.el index 1cfeb21..ca2422b 100644 --- a/ghpr-api.el +++ b/ghpr-api.el @@ -82,6 +82,28 @@ Returns a list of pull request objects on success, nil on failure." (message "Error fetching pull requests: %s" error-thrown))))) result)) +(defun ghpr--get-pr (repo-name pr-number) + "Retrieve a single pull request by PR-NUMBER in REPO-NAME. +Returns a parsed PR object on success, nil on failure." + (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+json") + ("Authorization" . ,(format "Bearer %s" token)) + ("X-GitHub-Api-Version" . "2022-11-28")) + :parser 'json-read + :sync t + :success (cl-function + (lambda (&key data &allow-other-keys) + (setq result (ghpr--parse-api-pr data)))) + :error (cl-function + (lambda (&key error-thrown &allow-other-keys) + (message "Error fetching PR: %s" error-thrown))))) + result)) + (defun ghpr--get-diff-content (repo-name pr-number) "Retrieve the diff contents for PR-NUMBER in REPO-NAME." (let ((token (ghpr--get-token)) diff --git a/ghpr.el b/ghpr.el index 6bba261..6e780bf 100644 --- a/ghpr.el +++ b/ghpr.el @@ -54,6 +54,15 @@ (let ((pr (cdr (assoc selected-item pr-items)))) (ghpr--open-pr pr repo-name)))))))) +(defun ghpr-open-pr (pr-number) + "Open a specific pull request by PR-NUMBER." + (interactive "nPR number: ") + (let* ((repo-name (ghpr--get-repo-name)) + (pr (when repo-name (ghpr--get-pr repo-name pr-number)))) + (if pr + (ghpr--open-pr pr repo-name) + (message "Could not open PR #%d" pr-number)))) + (provide 'ghpr) ;;; ghpr.el ends here From 3044a2611aea6182f867138d58a2d9ce8a558b11 Mon Sep 17 00:00:00 2001 From: Lucas Sta Maria Date: Wed, 27 Aug 2025 16:43:48 +0800 Subject: [PATCH 2/4] fix: store entire base content --- ghpr-review.el | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ghpr-review.el b/ghpr-review.el index 4e7a98c..e1e9d28 100644 --- a/ghpr-review.el +++ b/ghpr-review.el @@ -68,14 +68,14 @@ ("^\\(> \\)$" (0 'ghpr-review-misc-line t))) "Font lock keywords for ghpr-review-mode.") -(defvar-local ghpr--review-diff-content nil - "Buffer-local variable storing the diff content for the current PR.") +(defvar-local ghpr--review-base-content nil + "Stores the base starting content, including diff, for the current PR.") (defvar-local ghpr--review-pr-metadata nil - "Buffer-local variable storing the PR metadata for the current PR.") + "Stores the PR metadata for the current PR.") (defvar-local ghpr--review-repo-name nil - "Buffer-local variable storing the repository name for the current PR.") + "Stores the repository name for the current PR.") (defvar ghpr-review-mode-map (let ((map (make-sparse-keymap)) @@ -111,7 +111,7 @@ "Return a string of the title, body, and patch content separated by newlines." (let* ((body (or (alist-get 'body pr) "")) (content-parts (list (ghpr--pr-summary pr)))) - (when (not (string-empty-p body)) + (when (and body (not (string-empty-p body))) (push body content-parts)) (when diff-content (push diff-content content-parts)) @@ -123,7 +123,7 @@ (let* ((number (alist-get 'number pr)) (diff-content (ghpr--get-diff-content repo-name number)) (contents (ghpr--open-pr/collect-contents pr diff-content))) - (setq ghpr--review-diff-content diff-content) + (setq ghpr--review-base-content contents) (setq ghpr--review-pr-metadata pr) (setq ghpr--review-repo-name repo-name) (insert (ghpr--prefix-lines contents)))) From 22007c8a757cc4ec5037d9a2c66a43655292cad0 Mon Sep 17 00:00:00 2001 From: Lucas Sta Maria Date: Wed, 27 Aug 2025 17:04:43 +0800 Subject: [PATCH 3/4] fix: validate contents before submitting --- ghpr-review.el | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/ghpr-review.el b/ghpr-review.el index e1e9d28..82f37e9 100644 --- a/ghpr-review.el +++ b/ghpr-review.el @@ -122,11 +122,11 @@ (erase-buffer) (let* ((number (alist-get 'number pr)) (diff-content (ghpr--get-diff-content repo-name number)) - (contents (ghpr--open-pr/collect-contents pr diff-content))) + (contents (ghpr--prefix-lines (ghpr--open-pr/collect-contents pr diff-content)))) (setq ghpr--review-base-content contents) (setq ghpr--review-pr-metadata pr) (setq ghpr--review-repo-name repo-name) - (insert (ghpr--prefix-lines contents)))) + (insert contents))) (defun ghpr--open-pr (pr repo-name) "Open a new buffer containing the body of the PR." @@ -339,6 +339,14 @@ Returns an alist with comment, file-path, commit-sha, and diff-position, or nil (commit-sha . ,(alist-get 'commit-sha context)) (diff-position . ,(alist-get 'diff-position context))))))) +(defun ghpr--validate-base-content-unchanged () + "Validate that all base content (lines with > or < prefixes) remains unchanged. +Returns t if validation passes, nil otherwise." + (let* ((current-lines (split-string (buffer-string) "\n")) + (base-lines (split-string ghpr--review-base-content "\n")) + (current-base-lines (seq-filter #'ghpr--special-line-p current-lines))) + (equal current-base-lines base-lines))) + (defun ghpr--collect-review-comments () "Collect all inline review comments from the current buffer. Returns an alist of comments with their associated diff lines and GitHub API context. @@ -367,6 +375,9 @@ Collects review body and inline comments from current buffer." (error "No PR metadata found in buffer")) (unless ghpr--review-repo-name (error "No repository name found in buffer")) + ;; TODO: needs better usability. maybe highlight which lines were broken/missing and restore. + (unless (ghpr--validate-base-content-unchanged) + (error "Base content has been modified. Please restore the original diff content before submitting")) (let* ((body (ghpr--collect-review-body)) (inline-comments (ghpr--collect-review-comments)) From 9db3929117e025b05a2db6e1e1c5c8cb43a2458b Mon Sep 17 00:00:00 2001 From: Lucas Sta Maria Date: Wed, 27 Aug 2025 17:17:36 +0800 Subject: [PATCH 4/4] fix: confirm review submission and kill buffer on submit --- ghpr-review.el | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ghpr-review.el b/ghpr-review.el index 82f37e9..5961798 100644 --- a/ghpr-review.el +++ b/ghpr-review.el @@ -393,13 +393,17 @@ Collects review body and inline comments from current buffer." (or (not body) (string-empty-p (string-trim body)))) (error "Review body is required for %s events" event)) - (unless (ghpr--create-review ghpr--review-repo-name - pr-number - commit-sha - (or body "") - event - api-comments) - (message "Failed to submit review")))) + (when (yes-or-no-p (format "Submit review (%s)? " event)) + (if (ghpr--create-review ghpr--review-repo-name + pr-number + commit-sha + (or body "") + event + api-comments) + (progn + (message "Review submitted successfully") + (kill-buffer (current-buffer))) + (message "Failed to submit review"))))) (defun ghpr-review-comment () "Submit review comments with COMMENT event."