diff --git a/README.org b/README.org index 5e78a39..5ffdeef 100644 --- a/README.org +++ b/README.org @@ -46,10 +46,6 @@ Planned features: ** Example format - * =>= prefixes PR metadata and diff content from GitHub - * =<= prefixes existing review comments (to be implemented) - * Lines without prefixes are your review content: the review body above the first =>=, and individual inline comments in the diff sections - #+begin_src This is the review's body. Markdown *is* supported throughout the body and your @@ -59,8 +55,6 @@ by a single newline are joined together. Paragraphs are two newlines of separation. > [#241] @author: PR title > -> The description of the PR. -> > diff --git a/test.md b/test.md > index aa9e6ef..ae07d61 100644 > --- a/test.md diff --git a/ghpr-prs.el b/ghpr-prs.el deleted file mode 100644 index 2abc48c..0000000 --- a/ghpr-prs.el +++ /dev/null @@ -1,181 +0,0 @@ -;;; ghpr-prs.el --- PR list buffer functionality -*- lexical-binding: t -*- - -;; 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 . - - -;;; Commentary: - -;; ghpr-prs.el provides buffer-based PR list functionality using magit-section -;; for interactive navigation and selection of pull requests. - -;;; Code: - -(require 'magit) -(require 'magit-section) -(require 'ghpr-api) -(require 'ghpr-utils) -(require 'ghpr-repo) - -(defvar-keymap ghpr-pr-section-map - :doc "Keymap for PR sections." - :parent magit-section-mode-map - "RET" #'ghpr-show-pr - " " #'ghpr-show-pr) - -(defvar ghpr-prs-mode-map - (let ((map (make-sparse-keymap))) - (define-key map "q" 'quit-window) - (define-key map "g" 'ghpr-prs-refresh) - (define-key map (kbd "RET") 'ghpr-show-pr) - map) - "Keymap for `ghpr-prs-mode'.") - -(define-derived-mode ghpr-prs-mode magit-mode "GHPR PRs" - "Major mode for listing GitHub pull requests." - :group 'ghpr - :keymap ghpr-prs-mode-map - (hack-dir-local-variables-non-file-buffer) - (setq truncate-lines t)) - -(defvar-local ghpr--prs-repo-name nil - "Repository name for the current PR list buffer.") - -(defun ghpr--prs-setup-buffer (repo-name) - "Create and setup the PR list buffer for REPO-NAME, or reuse existing buffer." - (let* ((buffer-name (format "*ghpr-prs: %s*" repo-name)) - (existing-buffer (get-buffer buffer-name)) - (buffer (or existing-buffer (get-buffer-create buffer-name)))) - (with-current-buffer buffer - (unless existing-buffer - (ghpr-prs-mode) - (setq ghpr--prs-repo-name repo-name)) - (ghpr--prs-refresh-buffer)) - buffer)) - -(defun ghpr--prs-refresh-buffer (&optional error-message) - "Refresh the PR list buffer content, preserving cursor position." - (unless ghpr--prs-repo-name - (error "No repository name set in buffer")) - (let ((current-section (ghpr--get-current-pr)) - (prs+error (ghpr--fetch-prs-with-error-handling error-message))) - (ghpr--rebuild-buffer-content (car prs+error) (cdr prs+error)) - (ghpr--restore-cursor-or-top current-section))) - -(defun ghpr--get-current-pr () - "Get the current PR value to restore cursor position after refresh." - (and (magit-current-section) - (oref (magit-current-section) value))) - -(defun ghpr--fetch-prs-with-error-handling (error-message) - "Fetch PRs, returning (prs . error-message) cons cell." - (if error-message - (cons nil error-message) - (condition-case err - (cons (ghpr--list-open-prs ghpr--prs-repo-name) nil) - (error (cons nil (error-message-string err)))))) - -(defun ghpr--rebuild-buffer-content (prs error-message) - "Rebuild buffer content with PRS and optional ERROR-MESSAGE." - (let ((inhibit-read-only t)) - (erase-buffer) - (ghpr--insert-error-if-present error-message) - (ghpr--insert-prs-section prs error-message))) - -(defun ghpr--insert-error-if-present (error-message) - "Insert error message at top of buffer if ERROR-MESSAGE is non-nil." - (when error-message - (insert (propertize (format "Error: %s\n\n" error-message) - 'face 'error)))) - -(defun ghpr--insert-prs-section (prs error-message) - "Insert the main PRs section with PRS list or appropriate message." - (magit-insert-section (prs-buffer) - (magit-insert-heading (format "Pull Requests for %s" ghpr--prs-repo-name)) - (cond - ((and prs (not error-message)) - (dolist (pr prs) - (ghpr--insert-pr-section pr))) - (error-message - (insert "Could not fetch pull requests due to error above.\n")) - (t - (insert "No open pull requests found.\n"))))) - -(defun ghpr--restore-cursor-or-top (current-section) - "Restore cursor to CURRENT-SECTION if it exists, otherwise go to top." - (if current-section - (ghpr--restore-cursor-position current-section) - (goto-char (point-min)))) - -(defun ghpr--restore-cursor-position (target-pr) - "Restore cursor position to TARGET-PR if it still exists after refresh." - (goto-char (point-min)) - (unless (ghpr--find-and-goto-pr target-pr) - (goto-char (point-min)))) - -(defun ghpr--find-and-goto-pr (target-pr) - "Find TARGET-PR in buffer and move cursor to it. Return t if found." - (catch 'found - (while (not (eobp)) - (when (ghpr--current-line-matches-pr-p target-pr) - (throw 'found t)) - (forward-line 1)) - nil)) - -(defun ghpr--current-line-matches-pr-p (target-pr) - "Return t if current line contains a section matching TARGET-PR." - (let ((section (magit-current-section))) - (and section - (eq (oref section type) 'pr) - (equal (oref section value) target-pr)))) - -(defun ghpr-prs-refresh () - "Refresh the current PR list buffer." - (interactive) - (ghpr--prs-refresh-buffer)) - -(defun ghpr--insert-pr-section (pr) - "Insert a magit section for PR." - (let ((pr-summary (ghpr--pr-summary pr)) - (pr-number (alist-get 'number pr))) - (magit-insert-section (pr pr) - (oset (magit-current-section) keymap ghpr-pr-section-map) - (insert pr-summary) - (insert "\n")))) - -(defun ghpr-show-pr () - "Open the PR at point in review mode." - (interactive) - (let* ((section (magit-current-section)) - (pr (and section - (eq (oref section type) 'pr) - (oref section value))) - (prs-buffer (current-buffer))) - (if pr - (progn - (require 'ghpr-review) - (ghpr--open-pr pr ghpr--prs-repo-name prs-buffer)) - (user-error "No PR at point: section-type=%S, section-value=%S" - (and section (oref section type)) - (and section (oref section value)))))) - -(defun ghpr-prs-list (repo-name) - "Display a buffer listing open PRs for REPO-NAME." - (let ((buffer (ghpr--prs-setup-buffer repo-name))) - (switch-to-buffer buffer))) - -(provide 'ghpr-prs) - -;;; ghpr-prs.el ends here diff --git a/ghpr-review.el b/ghpr-review.el index 284f24d..35cf5eb 100644 --- a/ghpr-review.el +++ b/ghpr-review.el @@ -128,28 +128,16 @@ (setq ghpr--review-repo-name repo-name) (insert contents))) -;; FIXME: this feels like poor separation of concerns -(defun ghpr--open-pr (pr repo-name &optional prs-list-buffer) - "Open a new buffer containing the body of the PR. -If PRS-LIST-BUFFER is provided, kill it after successfully opening the PR." +(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))) - (condition-case err - (progn - (with-current-buffer buffer - (ghpr-review-mode) - (ghpr--open-pr/insert-contents pr repo-name) - (goto-char (point-min))) - (if prs-list-buffer - (switch-to-buffer buffer) - (switch-to-buffer-other-window buffer)) - (when (and prs-list-buffer (buffer-live-p prs-list-buffer)) - (kill-buffer prs-list-buffer))) - (error - (when (get-buffer buffer-name) - (kill-buffer buffer-name)) - (signal (car err) (cdr err)))))) + (with-current-buffer buffer + (ghpr-review-mode) + (ghpr--open-pr/insert-contents pr repo-name) + (goto-char (point-min))) + (switch-to-buffer-other-window buffer))) (defun ghpr--is-comment-line (line) "Return t if LINE is a user comment (no prefix), nil otherwise." diff --git a/ghpr.el b/ghpr.el index a3ca542..6e780bf 100644 --- a/ghpr.el +++ b/ghpr.el @@ -2,7 +2,7 @@ ;; Author: Lucas Sta Maria ;; Maintainer: Lucas Sta Maria -;; Version: 0.2 +;; Version: 0.1 ;; Package-Requires: (magit request) ;; Homepage: https://git.priime.dev/lucas/ghpr.el ;; Keywords: git @@ -36,17 +36,23 @@ (require 'ghpr-repo) (require 'ghpr-utils) (require 'ghpr-review) -(require 'ghpr-prs) (defun ghpr-prs () - "Display a buffer listing the current repository's open PRs." + "List and choose from the current repository's open PRs." (interactive) - (let ((repo-name (ghpr--get-repo-name))) + (let* ((repo-name (ghpr--get-repo-name)) + (prs (and repo-name (ghpr--list-open-prs repo-name)))) (cond ((not repo-name) (message "Not in a GitHub repository")) + ((not prs) + (message "No open pull requests found")) (t - (ghpr-prs-list repo-name))))) + (let* ((pr-items (mapcar #'ghpr--pr-summary-selection prs)) + (selected-item (completing-read "Select PR: " pr-items nil t))) + (when selected-item + (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."